我正在尝试识别或理解一种适当的技术(成语),以解决我遇到的特定并发编程问题。

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

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

我目前正在golang中实现此功能(相关代码段将在下面进行进一步介绍)。但总的来说,我正在解决以下问题:

  • 通过互斥waitCount保持对计数器int waitLock(请求我们表示“繁忙”的线程数)的R + W访问。
  • 函数drawStatus():重绘整个UI(每100毫秒发生一次):
  • 获取互斥体waitLock
  • 如果int waitCount> 0,则为
  • :
  • 绘制“忙”指示器
  • 释放互斥锁waitLock
  • 函数startWait():当线程需要指示忙时:
  • 获取互斥体waitLock
  • 增量int waitCount
  • 释放互斥锁waitLock
  • 函数stopWait():当线程不再需要指示忙时:
  • 获取互斥体waitLock
  • 递减int waitCount
  • 释放互斥锁waitLock

  • 对我来说,感觉好像我没有充分利用golang的并发功能,而没有使用我熟悉的互斥锁。但是,即使如此,此代码中仍有一个错误,其中“忙碌”指示器过早地被关闭。

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

    这是一些实现上述逻辑的医生片段

        var WaitCycle = [...]rune{'🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘'}
    
        // type Layout holds the high level components of the terminal user interface
        type Layout struct {
            //
            // ... other fields hidden for example ...
            //
            waitLock  sync.Mutex
            waitIndex int // the current index of the "busy" rune cycle
            waitCount int // the current number of tasks enforcing the "busy" state
        }
    
        // function show() starts drawing the user interface.
        func (l *Layout) show() *ReturnCode {
    
            // timer forcing a redraw @ 10Hz
            go func(l *Layout) {
                tick := time.NewTicker(100 * time.Millisecond)
                defer tick.Stop()
                for {
                    select {
                    case <-tick.C:
                        // forces the UI to redraw all changed screen regions
                        l.ui.QueueUpdateDraw(func() {})
                    }
                }
            }(l)
    
            if err := l.ui.Run(); err != nil {
                return rcTUIError.specf("show(): ui.Run(): %s", err)
            }
            return nil
        }
    
        // function drawStatus() draws the "Busy" indicator at a specific UI position
        func (l *Layout) drawStatus(...) {
    
            l.waitLock.Lock()
            if l.waitCount > 0 {
              l.waitIndex = (l.waitIndex + 1) % WaitCycleLength
              waitRune := fmt.Sprintf(" %c ", WaitCycle[l.waitIndex])
              drawToScreen(waitRune, x-1, y, width)
            }
            l.waitLock.Unlock()
        }
    
        // function startWait() safely fires off the "Busy" indicator on the status bar
        // by resetting the current index of the status rune cycle and incrementing the
        // number of goroutines requesting the "Busy" indicator.
        func (l *Layout) startWait() {
            l.waitLock.Lock()
            if 0 == l.waitCount {
              l.waitIndex = 0
            }
            l.waitCount++
            l.waitLock.Unlock()
        }
    
        // function stopWait() safely hides the "Busy" indicator on the status bar by
        // decrementing the number of goroutines requesting the "Busy" indicator.
        func (l *Layout) stopWait() {
            l.waitLock.Lock()
            l.waitCount--
            l.waitLock.Unlock()
        }
    

    最佳答案

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

    关于multithreading - 如何限制对单个实时资源的并发访问,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53718146/

    10-09 23:15