The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
CarrieBauch

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

  •  
  •   CarrieBauch · Jul 16, 2023 · 2545 views
    This topic created in 1076 days ago, the information mentioned may be changed or developed.

    各位大佬,下面的 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 replies    2023-07-17 21:50:29 +08:00
    yankebupt
        1
    yankebupt  
       Jul 16, 2023   ❤️ 1
    我不懂 go ,但是我猜是这行的问题
    for len(queue) == 2 {
    是 for 还是 if 来的?
    yankebupt
        2
    yankebupt  
       Jul 16, 2023
    看了下,还真是 for
    CarrieBauch
        3
    CarrieBauch  
    OP
       Jul 16, 2023
    @yankebupt 这个地方是 for
    trzzzz
        4
    trzzzz  
       Jul 16, 2023
    len(queue) >= 2
    trzzzz
        5
    trzzzz  
       Jul 16, 2023
    @trzzzz 不好意思发错了
    AnroZ
        6
    AnroZ  
       Jul 16, 2023
    #来自网上的信息# 调用 Wait 会自动释放锁 c.L ,并挂起调用者所在的 goroutine ,因此当前协程会阻塞在 Wait 方法调用的地方。如果其他协程调用了 Signal 或 Broadcast 唤醒了该协程,那么 Wait 方法在结束阻塞时,会重新给 c.L 加锁,并且继续执行 Wait 后面的代码。
    thevita
        7
    thevita  
       Jul 16, 2023
    @AnroZ 对的,因为条件变量就是这么用的,或者说就是这么设计的
    thevita
        8
    thevita  
       Jul 16, 2023   ❤️ 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
       Jul 16, 2023
    @AnroZ 明白了,非常感谢
    Jooeeee
        10
    Jooeeee  
       Jul 17, 2023
    文档中的注释
    // 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:
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3090 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 109ms · UTC 08:38 · PVG 16:38 · LAX 01:38 · JFK 04:38
    ♥ Do have faith in what you're doing.