饥饿
饥饿
饥饿是指并发进程无法获得执行工作所需的任何资源的情况。当我们讨论活锁时,每个
下面这个例子展示了一个贪婪的
var wg sync.WaitGroup
var sharedLock sync.Mutex
const runtime = 1*time.Second
greedyWorker := func() {
defer wg.Done()
var count int
for begin := time.Now(); time.Since(begin) <= runtime; {
sharedLock.Lock()
time.Sleep(3*time.Nanosecond)
sharedLock.Unlock()
count++
}
fmt.Printf("Greedy worker was able to execute %v work loops\n", count)
}
politeWorker := func() {
defer wg.Done()
var count int
for begin := time.Now(); time.Since(begin) <= runtime; {
sharedLock.Lock()
time.Sleep(1*time.Nanosecond)
sharedLock.Unlock()
sharedLock.Lock()
time.Sleep(1*time.Nanosecond)
sharedLock.Unlock()
sharedLock.Lock()
time.Sleep(1*time.Nanosecond)
sharedLock.Unlock()
count++
}
fmt.Printf("Polite worker was able to execute %v work loops.\n", count)
}
wg.Add(2)
go greedyWorker()
go politeWorker()
wg.Wait()
// Polite worker was able to execute 289777 work loops. Greedy worker was able to execute 471287 work loops
我们在例子中使用计数的方式识别饥饿,在记录和抽样度量指标时这是一个很不错的方法。检测和解决饥饿的方法之一就是就是记录程序完成的时间,然后确定你的程序执行速度是否与预期的一样高。
值得一提的是,前面的代码示例也可以作为进行同步内存访问的性能分支示例。因为同步访问内存的代价很高,所以扩大我们的锁定范围可能会产生额外的代价。另一方面,正如我们所看到的那样,我们冒着令其他并发进程挨饿的风险。
如果你利用内存访问同步,你必须在性能粗粒度同步和公平性细粒度同步之间找到平衡点。当开始调试应用程序时,我强烈建议你将内存访问同步仅限于程序的关键部分
因此,饥饿可能会导致程序无效或不正确。前面的例子表明了执行效率是如何被降低的,如果你有一个非常贪婪的并发进程,以至于完全阻止另一个并发进程完成工作,那么你的问题就大了。我们还需要考虑来自程序之外导致的饥饿问题。请记住,饥饿还可以产生于于