1. Goroutines 和 Channels 简介
Goroutines 是 Go 语言中的一种机制,可以让程序开启并发执行的线程。Channels 是用来在 Goroutines 之间通信的一种数据结构。Channels 类似于管道,可以传递和同步多个 Goroutines 之间的消息。
2. Channels 的基本用法
2.1 创建和关闭 Channels
可以使用 make 函数来创建一个 Channel:
ch := make(chan int)
这里创建了一个类型为 int 的 Channel,名为 ch。
在 Channels 不再使用时,应该通过 close 函数来关闭它:
close(ch)
如果在尝试向已经关闭的 Channel 发送或接收数据,将会报错。
2.2 发送和接收数据
在一个 Goroutine 中,可以通过 Channel 的 <- 操作符来发送数据:
ch <- 10
这里将整数 10 发送给 Channel ch。
在另一个 Goroutine 中,可以通过 <-ch 操作符来接收数据:
data := <-ch
这里将从 Channel ch 中接收数据,并赋值给变量 data。
如果 Channel 没有数据可供接收,接收操作将会阻塞当前 Goroutine,直到有数据可用。
3. Channels 的同步用法
Channels 除了可以用来传递数据外,还可以用来同步多个 Goroutines 的执行。
3.1 等待多个 Goroutines 完成
可以使用 Channel 来等待多个 Goroutines 完成某个任务,例如:
func worker(ch chan<- bool) {
// Do some work...
ch <- true
}
func main() {
done := make(chan bool, 3)
for i := 0; i < 3; i++ {
go worker(done)
}
// Wait for all workers
for i := 0; i < 3; i++ {
<-done
}
}
这里创建了一个有缓冲的 Channel done,并在 main 函数中启动了三个 worker Goroutines。worker 函数在完成工作后,向 done Channel 发送一个 bool 类型的值 true。
在 main 函数中,使用 for 循环等待三个 worker Goroutines 都完成任务,这样就可以保证所有 worker Goroutines 都已经执行完毕。
3.2 信号量和互斥锁
Channels 还可以用来实现信号量和互斥锁。
如果希望限制一段代码的并发访问量,可以使用有缓冲的 Channel 作为信号量,例如:
sem := make(chan int, MaxConcurrent)
for {
sem <-1
go func() {
// Some work...
<-sem
}()
}
这里将一个大小为 MaxConcurrent 的有缓冲的 Channel 作为信号量,当某个 Goroutine 要执行敏感操作时,首先向信号量中发一个消息,表示占用了一个许可,然后执行敏感操作。敏感操作完成后,从信号量中读取一个消息,表示归还了一个许可,这样下一个 Goroutine 就可以继续执行敏感操作。
如果希望限制一段代码的并发访问量为 1,可以使用无缓冲的 Channel 作为互斥锁,例如:
var mutex = make(chan bool, 1)
// Critical section
func criticalSection() {
mutex <- true
// Critical code
<-mutex
}
这里定义了一个大小为 1 的无缓冲的 Channel 作为互斥锁,当某个 Goroutine 进入 criticalSection 函数时,首先从 mutex Channel 中获取一个许可。如果此时有另一个 Goroutine 正在执行 criticalSection 函数,则第二个 Goroutine 的获取许可操作将会被阻塞,直到第一个 Goroutine 释放互斥锁。
4. 总结
通过 Channels 可以方便地在 Goroutines 之间传递数据和同步执行。Channels 不仅可以用来传递简单数据,还可以用于高级并发控制,例如等待多个 Goroutines 完成、实现信号量和互斥锁等。