下面的 Go 代码中,为什么第 17 行的 c.L.Lock() 不会一直等待锁?

322 天前
 CarrieBauch

各位大佬,下面的 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()
	}
}

1455 次点击
所在节点    Go 编程语言
10 条回复
yankebupt
322 天前
我不懂 go ,但是我猜是这行的问题
for len(queue) == 2 {
是 for 还是 if 来的?
yankebupt
322 天前
看了下,还真是 for
CarrieBauch
322 天前
@yankebupt 这个地方是 for
trzzzz
322 天前
len(queue) >= 2
trzzzz
322 天前
@trzzzz 不好意思发错了
AnroZ
322 天前
#来自网上的信息# 调用 Wait 会自动释放锁 c.L ,并挂起调用者所在的 goroutine ,因此当前协程会阻塞在 Wait 方法调用的地方。如果其他协程调用了 Signal 或 Broadcast 唤醒了该协程,那么 Wait 方法在结束阻塞时,会重新给 c.L 加锁,并且继续执行 Wait 后面的代码。
thevita
322 天前
@AnroZ 对的,因为条件变量就是这么用的,或者说就是这么设计的
thevita
322 天前
这是基础的并发原语之一,各 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);
....
```
CarrieBauch
322 天前
@AnroZ 明白了,非常感谢
Jooeeee
321 天前
文档中的注释
// 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:

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/957178

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX