在Go中,如何使用互斥锁与Go例程进行锁定和等待

问题描述 投票:0回答:1

我的代码是这样的:

var lock sync.Mutex

func DoSomething() {
  lock.Lock()
  go func() {
    defer lock.Unlock()
    // Code here
  }()
}

func Wait() {
  lock.Lock()
  lock.Unlock()
}

我需要

Wait()
,因为有时我必须在
DoSomething()
panic
之前调用
sys.Exit
。所以,在这些情况下,我需要它来完成。

只要我一次只调用

DoSomething()
一次,它就可以正常工作,但是当然,如果调用两次,第二个会在调用 goroutine 之前等待。

DoSomething() // returns immediately
DoSomething() // waits until the first one finishes to return

如果我将锁放在 Goroutine 内部,那么

Wait()
在 Goroutine 启动之前就会被调用。

func DoSomething() {
  go func() {
    lock.Lock()
    defer lock.Unlock()
    // Code here
  }()
}

DoSomething()
Wait() // this finishes before the goroutine even starts

我在

sync
中尝试了一些其他的东西,但还没有找到有效的东西。

我什至尝试将 goroutine 从

DoSomething
中取出并像
go DoSomething()
那样调用它,但我也无法让它工作。

go asynchronous mutex goroutine
1个回答
0
投票

免责声明:我仍在学习围棋。我不能 100% 确定这是惯用的方法(双关语)。


这个解决方案的想法是:我们不直接调用方法,而是:

  • 从一开始就启动一个 go 例程,它等待通道上的某些信号
  • 当我们想做某事时,我们会写信给频道
  • 为了尽可能与执行同步,我们可以在通道上传递另一个通道 - 后台通信通道

把它们放在一起,我们得到这样的结果:

package main

import "fmt"

// This is the channel used by doSomething() to be notified when it
var doSomethingChannel = make(chan chan<- interface{})

// This is the channel we expose as interface to the outside so we can trigger "doSomething" to... do something
func DoSomethingChannel() chan<- chan<- interface{} {
    return doSomethingChannel
}

func doSomething() {
    for {
        back := <- doSomethingChannel
        // code goes here
        fmt.Println("[GOROUTINE] Executing")

        // if the back-channel is set, i.e. if the caller wants to be informed, write a signal on the back-channel
        if back != nil {
            back<- struct{}{}
        }
    }
}

func main() {
    go doSomething()
    for i := 0; i < 11; i += 1 {
        // if i is even, we want to wait, otherwise we do not want to wait
        var back chan interface{} = nil
        if i % 2 == 0 {
            back = make(chan interface{})
            fmt.Printf("[CALLER] Waiting for execution %d\n", i)
        } else {
            fmt.Printf("[CALLER] Calling for execution %d, will not wait\n", i)
        }
        // send the back-channel to the DoSomethingChannel, this will trigger doSomething()
        DoSomethingChannel() <- back
        if back != nil {
            // Wait if back-channel was used
            <-back
            fmt.Printf("[CALLER] %d has been done\n", i)
        }
    }
}

go.dev
演示

© www.soinside.com 2019 - 2024. All rights reserved.