Channel通道
Channel基础
Channel是Go语言中用于goroutine之间通信的管道。本节介绍Channel的基本概念和使用方法。
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个无缓冲的channel
ch := make(chan string)
// 启动一个goroutine发送数据
go func() {
ch <- "Hello from goroutine!"
}()
// 主goroutine接收数据
msg := <-ch
fmt.Println(msg)
// 创建一个带缓冲的channel
bufferedCh := make(chan int, 3)
// 发送数据到缓冲channel
bufferedCh <- 1
bufferedCh <- 2
bufferedCh <- 3
// 接收数据
fmt.Println(<-bufferedCh) // 1
fmt.Println(<-bufferedCh) // 2
fmt.Println(<-bufferedCh) // 3
}
Channel操作
Channel支持发送、接收、关闭等基本操作,本节展示这些操作的使用方法。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
// 发送操作
go func() {
for i := 0; i < 5; i++ {
ch <- i
time.Sleep(time.Millisecond * 100)
}
close(ch) // 关闭channel
}()
// 接收操作
for {
if val, ok := <-ch; ok {
fmt.Println("Received:", val)
} else {
fmt.Println("Channel closed")
break
}
}
// 使用range接收
ch2 := make(chan string)
go func() {
ch2 <- "Hello"
ch2 <- "World"
close(ch2)
}()
for msg := range ch2 {
fmt.Println(msg)
}
}
Select语句
Select语句用于在多个channel操作中进行选择,实现非阻塞的通信。
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(time.Second)
ch1 <- "from ch1"
}()
go func() {
time.Sleep(time.Second * 2)
ch2 <- "from ch2"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
case <-time.After(time.Second * 3):
fmt.Println("timeout")
}
}
}
Channel方向
Channel可以是单向的(只读或只写),用于限制channel的使用方式。
package main
import "fmt"
// 只写channel
func sendData(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
// 只读channel
func receiveData(ch <-chan int) {
for val := range ch {
fmt.Println("Received:", val)
}
}
func main() {
ch := make(chan int)
go sendData(ch)
receiveData(ch)
}
Channel超时控制
使用select和time.After实现channel操作的超时控制。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
// 模拟耗时操作
go func() {
time.Sleep(time.Second * 2)
ch <- "operation completed"
}()
// 设置超时
select {
case result := <-ch:
fmt.Println(result)
case <-time.After(time.Second):
fmt.Println("operation timed out")
}
}
Channel与Context
结合Context包实现channel的取消和超时控制。
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, ch chan<- int) {
for i := 0; ; i++ {
select {
case <-ctx.Done():
fmt.Println("worker cancelled")
return
case ch <- i:
time.Sleep(time.Millisecond * 100)
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
ch := make(chan int)
go worker(ctx, ch)
for {
select {
case <-ctx.Done():
fmt.Println("main cancelled")
return
case val := <-ch:
fmt.Println("Received:", val)
}
}
}
Channel最佳实践
使用channel时的一些重要注意事项和最佳实践。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
// 1. 使用WaitGroup等待goroutine完成
var wg sync.WaitGroup
ch := make(chan int)
// 2. 避免goroutine泄漏
go func() {
defer wg.Done()
for val := range ch {
fmt.Println("Processing:", val)
}
}()
// 3. 使用缓冲channel提高性能
bufferedCh := make(chan int, 10)
// 4. 正确关闭channel
go func() {
for i := 0; i < 5; i++ {
bufferedCh <- i
}
close(bufferedCh)
}()
// 5. 使用select实现非阻塞操作
select {
case val := <-bufferedCh:
fmt.Println("Received:", val)
default:
fmt.Println("No data available")
}
// 6. 使用time.After实现超时
select {
case val := <-ch:
fmt.Println(val)
case <-time.After(time.Second):
fmt.Println("Timeout")
}
}