Golang 中 Goroutines 之间如何通过 Channels 进行同步

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 完成、实现信号量和互斥锁等。

后端开发标签