golang channel Synchronization

发布时间 2023-07-12 13:55:11作者: 快乐嘉年华

在 Go 语言中,通道(channel)是一个很重要的并发同步机制,可以用来在不同的 goroutine 之间发送和接收数据。

通道实现了一个先进先出(FIFO)的数据结构,所以可以确保数据的接收顺序与发送顺序一致。此外,通道的发送和接收操作都是原子的,这意味着你不需要额外的锁来同步数据访问。
这里有几种不同的方式可以用通道来同步 goroutines:

等待消息
你可以在一个 goroutine 中发送数据,然后在另一个 goroutine 中接收数据。接收操作会阻塞,直到有数据可以接收。

message := make(chan string)

go func() {
    message <- "ping"
}()

msg := <-message
fmt.Println(msg)

在上面的例子中,主 goroutine 会在 msg := <-message 这一行阻塞,直到子 goroutine 向 message 通道发送了数据。

等待通道关闭
发送者可以关闭通道来表示不会有更多的数据发送了。接收者可以使用 for-range 循环来接收数据,直到通道被关闭。

package main

import "fmt"

func main() {
	jobs := make(chan int, 5)
	done := make(chan bool)

	go func() {
		for {
			_, more := <-jobs
			if more {
				fmt.Println("received job")
			} else {
				fmt.Println("received all jobs")
				done <- true
				return
			}
		}
	}()

	for i := 1; i <= 3; i++ {
		jobs <- i
		fmt.Println("sent job", i)
	}
	close(jobs)
	fmt.Println("sent all jobs")

	<-done
}

在这个例子中,主 goroutine 会在最后的 <-done 这一行阻塞,直到子 goroutine 向 done 通道发送了数据。
使用通道来实现锁:虽然 Go 语言提供了 sync.Mutex 来实现互斥锁,但有时你也可以使用通道来实现类似的效果。例如,你可以创建一个只能存储一个元素的无缓冲通道,然后通过发送和接收操作来实现锁的获取和释放。

mutex := make(chan struct{}, 1)

mutex <- struct{}{} // 获取锁
// critical section
<-mutex // 释放锁

在这个例子中,尝试向 mutex 通道发送数据会阻塞,直到通道中的数据被接收。这就实现了类似于互斥锁的效果。