各位大佬,下面的 Go 代码中,当主 Goroutine 执行到 c.Wait() 的时候,第 28 行的 c.L.Lock() 肯定执行了,那么当执行到第 17 行的 c.L.Lock(),为什么程序不会一直阻塞呢?
package main import ( "fmt" "sync" "time" ) // a goroutine that is waiting for a signal, and a goroutine that is sending signals. // Say we have a queue of fixed length 2, and 10 items we want to push onto the queue func main() { c := sync.NewCond(&sync.Mutex{}) queue := make([]interface{}, 0, 10) removeFromQueue := func(delay time.Duration) { time.Sleep(delay) c.L.Lock() // 这是第 17 行,执行到这里为什么不是一直阻塞等待锁? queue = queue[1:] fmt.Println("Removed from queue") c.L.Unlock() c.Signal() // let a goroutine waiting on the condition know that something has ocurred } for i := 0; i < 10; i++ { c.L.Lock() // 这是 28 行,critical section // When the queue is equal to two the main goroutine is suspend // until a signal on the condition has been sent length := len(queue) fmt.Println(length) for len(queue) == 2 { fmt.Println("wait signal") c.Wait() // 这是 36 行,等待 signal ,但是 removeFromQueue 为什么不会一直等待锁呢? } fmt.Println("Adding to queue") queue = append(queue, struct{}{}) go removeFromQueue(10 * time.Second) c.L.Unlock() } }
![]() | 1 yankebupt 2023-07-16 18:47:12 +08:00 ![]() 我不懂 go ,但是我猜是这行的问题 for len(queue) == 2 { 是 for 还是 if 来的? |
![]() | 2 yankebupt 2023-07-16 18:49:57 +08:00 看了下,还真是 for |
3 CarrieBauch OP @yankebupt 这个地方是 for |
![]() | 4 trzzzz 2023-07-16 18:51:30 +08:00 len(queue) >= 2 |
![]() | 6 AnroZ 2023-07-16 19:04:53 +08:00 #来自网上的信息# 调用 Wait 会自动释放锁 c.L ,并挂起调用者所在的 goroutine ,因此当前协程会阻塞在 Wait 方法调用的地方。如果其他协程调用了 Signal 或 Broadcast 唤醒了该协程,那么 Wait 方法在结束阻塞时,会重新给 c.L 加锁,并且继续执行 Wait 后面的代码。 |
8 thevita 2023-07-16 19:15:53 +08:00 ![]() 这是基础的并发原语之一,各 api 下设计都类似 eg. cpp: https://en.cppreference.com/w/cpp/thread/condition_variable pthread: ``` .... int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); .... ``` |
9 CarrieBauch OP @AnroZ 明白了,非常感谢 |
10 Jooeeee 2023-07-17 21:50:29 +08:00 文档中的注释 // Wait atomically unlocks c.L and suspends execution // of the calling goroutine. After later resuming execution, // Wait locks c.L before returning. Unlike in other systems, // Wait cannot return unless awoken by Broadcast or Signal. // // Because c.L is not locked while Wait is waiting, the caller // typically cannot assume that the condition is true when // Wait returns. Instead, the caller should Wait in a loop: |