首页 > 解决方案 > 如果 goroutine 在获取 Signal() 或 Broadcast() 后无法获取锁,是否会返回等待模式

问题描述

假设我有以下功能。

func acquireResourceAndDoSomething(resourceA, resourceB int) {
    lock.Lock()
    for !isBothResourceAvailable(resourceA, resourceB) {
        isBothResourceAvailable.Wait()
    }

    isResourceUsed[resourceA] = true
    isResourceUsed[resourceB] = true

    fmt.Printf("Doing something with resource #%v and #%v\n", resourceA, resourceB)

    lock.Unlock()
}

如果我有 3 个 gorountine G1、G2 和 G3。

假设 G3 正在工作并持有资源 B 和 D,而 G1 和 G2 在循环中等待。最终,当 G3 完成并Broadcast()在释放 B 和 D 后调用时,我希望 G1 和 G2 同时唤醒并同时检查条件。显然,他们中只有 1 人会得到锁继续前进。

我的问题是假设 G1 是第一个获得锁以继续获取资源 A 和 B 的人。如果 G1Unlock()在没有释放 A 和 B 的情况下调用,G2 会醒来,获得锁并继续,就好像资源 B 和 C 可用一样还是 G2 会再次检查条件以再次进入等待模式?

我是 Golang 的新手,所以非常感谢您的解释:)。

标签: goconcurrency

解决方案


Go routine等待获取锁时,释放时第一次获取。但是如果有一个Go routines pool,随机一个是获取锁。

但它不知道内部发生了什么。在函数内部,它获得了锁,不管它是否可以等待。它由运行时条件决定。

考虑下面的简单代码

您可以使用两个锁并模拟如下场景。在函数内部,Go 例程必须等待获得另一个锁。它进入函数内部并等待第二个锁。它不知道 lock2 和等待时间,直到获得 lock 1 并在代码之后运行。

package main

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

var lock = new(sync.Mutex)
var lock2 = new(sync.Mutex)

func main() {
    wg := new(sync.WaitGroup)
    for i := 0; i < 3; i++ {
        i := i
        wg.Add(1)
        go test(wg, i)
    }

    wg.Wait()
}

func test(wg *sync.WaitGroup, i int) {
    defer wg.Done()
    fmt.Printf("waiting for lock 1 at G%d\n", i)
    t := time.Now().UnixNano()
    lock.Lock()
    fmt.Printf("acquired lock 1 at G%d after %d nano secs \n\n", i, time.Now().UnixNano()-t)
    lock.Unlock()

    fmt.Printf("waiting for lock 2 at G%d\n", i)
    t = time.Now().UnixNano()
    lock2.Lock()
    time.Sleep(time.Second)
    fmt.Printf("acquired lock 2 at G%d after %d nano secs \n", i, time.Now().UnixNano()-t)
    lock2.Unlock()

    fmt.Printf("Done at G%d\n\n", i)
}

用于证明行为的随机输出之一。这里所有的 Go 例程都获得了第一个锁,但等待每个 Go 例程解锁第二个锁。所以,直到进入函数它才知道第二个锁

waiting for lock 1 at G2
acquired lock 1 at G2 after 0 nano secs 

waiting for lock 2 at G2
waiting for lock 1 at G1
acquired lock 1 at G1 after 0 nano secs 

waiting for lock 2 at G1
waiting for lock 1 at G0
acquired lock 1 at G0 after 0 nano secs 

waiting for lock 2 at G0
acquired lock 2 at G2 after 1000000000 nano secs 
Done at G2

acquired lock 2 at G1 after 2000000000 nano secs 
Done at G1

acquired lock 2 at G0 after 3000000000 nano secs 
Done at G0

操场上奔跑


推荐阅读