Golang chan 关键字
最后修改于 2025 年 5 月 7 日
本教程将解释如何在 Go 中使用 chan
关键字。我们将通过并发编程的实际示例来介绍通道基础知识。
chan
关键字用于声明一个通道类型,用于 goroutine 之间的通信。通道是类型化的管道,允许你发送和接收值。
在 Go 中,chan
是安全并发编程的基础。通道通过设计来同步 goroutine 并防止竞态条件。
基本通道创建
chan
最简单的用法是创建一个无缓冲通道。本示例演示了基本的通道声明和用法。
package main import "fmt" func main() { // Create an unbuffered channel of integers ch := make(chan int) go func() { // Send value through channel ch <- 42 }() // Receive value from channel val := <-ch fmt.Println("Received:", val) }
通道 ch
在 goroutine 之间传输一个整数。主 goroutine 会阻塞,直到接收到该值。
缓冲通道
缓冲通道允许在不阻塞的情况下发送多个值。本示例展示了如何创建和使用它们。
package main import "fmt" func main() { // Create a buffered channel with capacity 3 ch := make(chan string, 3) // Send values without blocking ch <- "first" ch <- "second" ch <- "third" // Receive values fmt.Println(<-ch) fmt.Println(<-ch) fmt.Println(<-ch) }
通道在阻塞之前可以容纳三个字符串。值按 FIFO(先进先出)顺序接收。缓冲通道可以解耦发送者和接收者。
通道方向
通道参数可以限制为仅发送或仅接收。本示例演示了定向通道。
package main import "fmt" // sendOnly can only send to channel func sendOnly(ch chan<- int, val int) { ch <- val } // receiveOnly can only receive from channel func receiveOnly(ch <-chan int) int { return <-ch } func main() { ch := make(chan int) go sendOnly(ch, 99) val := receiveOnly(ch) fmt.Println("Received:", val) }
定向通道使函数契约更清晰。chan<-
表示仅发送,而 <-chan
表示仅接收。
通道同步
通道可以同步 goroutine。本示例展示了一种常见的 worker 模式。
package main import ( "fmt" "time" ) func worker(done chan bool) { fmt.Print("Working...") time.Sleep(time.Second) fmt.Println("done") done <- true } func main() { done := make(chan bool) go worker(done) // Block until worker finishes <-done fmt.Println("Worker completed") }
主 goroutine 等待 worker 发送完成信号。通道提供简单的同步,无需锁。
带通道的 Select 语句
select
语句处理多个通道操作。本示例演示了非阻塞通道操作。
package main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(1 * time.Second) ch1 <- "one" }() go func() { time.Sleep(2 * time.Second) ch2 <- "two" }() for i := 0; i < 2; i++ { select { case msg1 := <-ch1: fmt.Println("Received", msg1) case msg2 := <-ch2: fmt.Println("Received", msg2) } } }
select
语句会等待多个通道操作。它会执行第一个就绪的 case。这对于超时和多路复用很有用。
通道超时
通道可以使用 select
实现超时。本示例展示了如何防止无限阻塞。
package main import ( "fmt" "time" ) func main() { ch := make(chan string) go func() { time.Sleep(3 * time.Second) ch <- "result" }() select { case res := <-ch: fmt.Println(res) case <-time.After(2 * time.Second): fmt.Println("timeout") } }
time.After
创建一个在指定时间后发送值的通道。Select 语句会在结果和超时之间进行选择。
关闭通道
关闭通道表示不再发送值。本示例演示了正确的通道关闭方法。
package main import "fmt" func producer(ch chan int) { for i := 0; i < 5; i++ { ch <- i } close(ch) } func main() { ch := make(chan int) go producer(ch) for { val, ok := <-ch if !ok { fmt.Println("Channel closed") break } fmt.Println("Received:", val) } }
生产者在发送完所有值后关闭通道。接收者可以使用第二个返回值检测通道是否已关闭。
来源
本教程通过通道在并发编程中的实际用法示例,讲解了 Go 中的 chan
关键字。