如何限制对单个实时资源的并发访问

我正在尝试识别或理解适用于我遇到的特定并发编程问题的技术、惯用语。

为简单起见,假设我有一个实时图形用户界面 (UI),它始终以 10Hz 的频率在屏幕上重绘。

每当一组不同线程的至少一个实例正在运行时,我想在此 UI 上显示一个“忙碌”指示器,并且我希望该指示器在这些线程中正好有 0 个正在运行时停止显示。只要 UI 启动,这些线程就可以随时启动和停止。

我目前正在 golang 中实现这个(下面有相关的片段)。但总的来说,我按如下方式解决这个问题:

  • 通过 mutex 保护对计数器 int waitCount(请求我们指示“繁忙”的线程数)的R+W 访问waitLock

  • 功能drawStatus():重绘整个 UI(每 100 毫秒发生一次):

    1. 绘制“忙碌”指示器

    1. 获取互斥体waitLock

    2. 如果整数waitCount> 0:

    3. 释放互斥体waitLock

  • 功能startWait():当一个线程需要指示忙碌时:

    1. 获取互斥体waitLock

    2. 增量整数waitCount

    3. 释放互斥体waitLock

  • 功能stopWait():当一个线程不再需要指示繁忙时:

    1. 获取互斥体waitLock

    2. 递减整数waitCount

    3. 释放互斥体waitLock

对我来说,感觉我没有充分利用 golang 的并发功能并诉诸于我熟悉的互斥体。但即便如此,此代码中仍存在一个错误,其中“忙碌”指示器会过早消失。

老实说,我不是在寻找任何人来帮助识别该错误,而是试图传达我感兴趣的特定逻辑。是否有更惯用的 golang 方法来解决这个问题?或者是否有我应该研究的更通用的编程模式?我正在使用的这项技术有任何特定的名称吗?关于正确执行此操作的建议或指示会很棒。谢谢。


回首忆惘然
浏览 125回答 2
2回答

qq_遁去的一_1

由于您所做的只是锁定单个计数器,因此您可以简化并仅使用sync/atomic包。AddInt32(&x, 1)在启动 goroutine 和AddInt32(&x, -1)结束时调用。从您的绘图 goroutine调用LoadInt32(&x)。

慕容708150

它依赖于用例(你可以选择你想要的,并且在你产生错误或达到性能损失之前没有人关心),通道将 Lock 隐藏在里面并使编码更简单,但性能成本很小 - 所以我建议使用通道对于一般用例,除非您正在考虑更高的性能):在以下情况下使用通道:1 - 转让所有权2 - 协调在以下情况下使用基元:3 -性能关键4 - 保护结构引用的内部状态:第 33 页由于您使用的是协调 goroutine 数量的软实时 UI,而不是性能关键代码,因此我建议使用通道,我在此示例中简化了您的代码:package mainimport (    "fmt"    "math/rand"    "time")func main() {    for i := 0; i < 100; i++ {        go job() // e.g.: run all jobs    }    busy := 0    time.Sleep(10 * time.Millisecond) // or make sure at least on goroutine started    // 10Hz:    tick := time.NewTicker(100 * time.Millisecond)    defer tick.Stop()    for {        select {        case n := <-ch:            busy += n        case <-tick.C:            // forces the UI to redraw all changed screen regions            fmt.Printf(" %d    \r", busy)            if busy == 0 {                return            }        }    }}func job() {    ch <- +1    time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)    ch <- -1}var ch = make(chan int, 1)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go