我正在尝试识别或理解一种适当的技术(成语),以解决我遇到的特定并发编程问题。
为了简单起见,假设我有一个实时图形用户界面(UI),该界面始终以10Hz的频率重新绘制在屏幕上。
每当一组不同线程的至少一个实例正在运行时,我想在此UI上显示“忙碌”指示器,并且我希望当这些线程中恰好有0个正在运行时,该指示器停止显示。只要用户界面启动,这些线程就可以随时启动和停止。
我目前正在golang中实现此功能(相关代码段将在下面进行进一步介绍)。但总的来说,我正在解决以下问题:
waitCount
保持对计数器int waitLock
(请求我们表示“繁忙”的线程数)的R + W访问。 drawStatus()
:重绘整个UI(每100毫秒发生一次):waitLock
waitCount
> 0,则为waitLock
startWait()
:当线程需要指示忙时:waitLock
waitCount
waitLock
stopWait()
:当线程不再需要指示忙时:waitLock
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/