Golang中sync.Cond的wait()方法是如何工作的?

Golang中sync.Cond的wait()方法是如何工作的? 请看这段代码:

	c.L.Lock()
	for i <= 99 {
		c.Wait()
	}
	c.L.Unlock()

在 c.L.Lock() 命令和 c.Wait() 指令之间的某个时刻,其他 goroutine 可能会调用 c.Signal() 或 c.Broadcast()。

如果在 c.Wait() 执行之前,其他 goroutine 就已经调用了 c.Signal() 或 c.Broadcast()……那么这个 c.Wait() 会怎样?它会永远等待吗?之前发出的 c.Signal() 会像在通道中那样被记住吗?


更多关于Golang中sync.Cond的wait()方法是如何工作的?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

谢谢!我怀疑如果Signal()Broadcast()刚好在调用Wait()之前发生,它们就会丢失。

更多关于Golang中sync.Cond的wait()方法是如何工作的?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


如果一个goroutine在另一个goroutine调用cond.Signal()之后调用cond.Wait(),那么它将不会被唤醒。cond.Signal()只会通知在其运行之前已经调用cond.Wait()的其中一个goroutine。如果两者"同时"调用,调用Wait()的goroutine可能有机会将自己添加到等待列表中,但这并不保证。如果多个goroutine调用Wait(),实现方式会按顺序唤醒它们,但请注意该行为并非API的一部分。此过程不涉及任何通道。

在Go语言的sync.Cond中,Wait()方法的工作机制是:当调用Wait()时,它会释放当前持有的锁,并将当前goroutine放入条件变量的等待队列中,然后阻塞。当其他goroutine调用Signal()Broadcast()时,会唤醒等待队列中的一个或多个goroutine。被唤醒的goroutine在从Wait()返回前会重新获取锁。

关键点在于:Signal()Broadcast()的调用只有在Wait()已经执行并将goroutine加入等待队列后才有效。如果在Wait()执行之前就调用了Signal()Broadcast(),那么这次调用不会对后续的Wait()产生任何影响——它不会"记住"之前的信号。

在你的代码示例中:

  • 如果其他goroutine在c.Wait()执行之前调用了c.Signal()c.Broadcast(),那么当c.Wait()执行时,它会立即阻塞并等待下一个信号。
  • 之前的信号不会保留,条件变量没有像channel那样的缓冲机制来存储信号。

下面是一个完整的示例来说明这个行为:

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	var mu sync.Mutex
	cond := sync.NewCond(&mu)
	
	// Goroutine 1: 等待条件
	go func() {
		mu.Lock()
		fmt.Println("Goroutine 1: 获取锁,开始等待")
		
		// 故意延迟一下,让其他goroutine有机会先发送信号
		time.Sleep(100 * time.Millisecond)
		
		cond.Wait() // 这会阻塞,等待信号
		fmt.Println("Goroutine 1: 被唤醒,继续执行")
		mu.Unlock()
	}()
	
	// Goroutine 2: 提前发送信号
	go func() {
		time.Sleep(50 * time.Millisecond) // 确保在Wait()之前发送信号
		fmt.Println("Goroutine 2: 发送信号(在Wait之前)")
		cond.Signal()
	}()
	
	// Goroutine 3: 稍后再次发送信号来唤醒Goroutine 1
	go func() {
		time.Sleep(200 * time.Millisecond)
		fmt.Println("Goroutine 3: 发送信号(在Wait之后)")
		cond.Signal()
	}()
	
	time.Sleep(1 * time.Second)
	fmt.Println("程序结束")
}

在这个例子中:

  1. Goroutine 1获取锁后延迟100毫秒才调用Wait()
  2. Goroutine 2在50毫秒时就调用Signal()(在Wait()之前)
  3. Goroutine 3在200毫秒时再次调用Signal()(在Wait()之后)

输出会是:

Goroutine 1: 获取锁,开始等待
Goroutine 2: 发送信号(在Wait之前)
Goroutine 3: 发送信号(在Wait之后)
Goroutine 1: 被唤醒,继续执行
程序结束

可以看到,第一次信号(在Wait()之前)被忽略了,只有第二次信号成功唤醒了Goroutine 1。

因此,在Wait()之前发出的Signal()Broadcast()不会产生任何效果,Wait()会正常阻塞等待后续的信号。

回到顶部