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 关键字。