V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
CarrieBauch
V2EX  ›  Go 编程语言

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

  •  
  •   CarrieBauch · 2023-07-16 18:28:01 +08:00 · 1621 次点击
    这是一个创建于 500 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位大佬,下面的 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()
    	}
    }
    
    
    10 条回复    2023-07-17 21:50:29 +08:00
    yankebupt
        1
    yankebupt  
       2023-07-16 18:47:12 +08:00   ❤️ 1
    我不懂 go ,但是我猜是这行的问题
    for len(queue) == 2 {
    是 for 还是 if 来的?
    yankebupt
        2
    yankebupt  
       2023-07-16 18:49:57 +08:00
    看了下,还真是 for
    CarrieBauch
        3
    CarrieBauch  
    OP
       2023-07-16 18:50:31 +08:00
    @yankebupt 这个地方是 for
    trzzzz
        4
    trzzzz  
       2023-07-16 18:51:30 +08:00
    len(queue) >= 2
    trzzzz
        5
    trzzzz  
       2023-07-16 18:53:33 +08:00
    @trzzzz 不好意思发错了
    AnroZ
        6
    AnroZ  
       2023-07-16 19:04:53 +08:00
    #来自网上的信息# 调用 Wait 会自动释放锁 c.L ,并挂起调用者所在的 goroutine ,因此当前协程会阻塞在 Wait 方法调用的地方。如果其他协程调用了 Signal 或 Broadcast 唤醒了该协程,那么 Wait 方法在结束阻塞时,会重新给 c.L 加锁,并且继续执行 Wait 后面的代码。
    thevita
        7
    thevita  
       2023-07-16 19:11:57 +08:00
    @AnroZ 对的,因为条件变量就是这么用的,或者说就是这么设计的
    thevita
        8
    thevita  
       2023-07-16 19:15:53 +08:00   ❤️ 1
    这是基础的并发原语之一,各 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
        9
    CarrieBauch  
    OP
       2023-07-16 19:20:21 +08:00
    @AnroZ 明白了,非常感谢
    Jooeeee
        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:
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3470 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 11:01 · PVG 19:01 · LAX 03:01 · JFK 06:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.