自旋锁与争用:指数退避和队列锁的深入解析
1. 自旋锁性能分析
在多线程编程中,自旋锁是一种常见的同步机制。然而,不同的自旋锁算法在性能上存在显著差异。
TASLock(Test-And-Set Lock)算法表现不佳。在该算法中,自旋线程几乎每次都会遇到缓存未命中的情况,必须使用总线来获取新的但未改变的值。更糟糕的是,当持有锁的线程尝试释放锁时,由于总线被自旋线程垄断,可能会导致释放延迟。
TTASLock(Test-Test-And-Set Lock)算法在锁被线程 A 持有时表现较好。当线程 B 首次读取锁时,会发生缓存未命中,需要等待值加载到其缓存中。只要 A 持有锁,B 重复读取该值时每次都会命中缓存,不会产生总线流量,也不会减慢其他线程的内存访问速度。而且,释放锁的线程不会被自旋线程延迟。
但当锁被释放时,情况会恶化。锁持有者通过将锁变量写为 false 来释放锁,这会立即使自旋线程的缓存副本失效。每个线程都会发生缓存未命中,重新读取新值,并几乎同时调用 getAndSet() 来获取锁。第一个成功的线程会使其他线程的缓存失效,其他线程必须再次读取值,从而引发总线流量风暴。最终,线程会再次进入本地自旋状态。
本地自旋的概念,即线程重复读取缓存值而不是重复使用总线,是设计高效自旋锁的重要原则。
2. 指数退避算法
为了改进 TTASLock 算法,引入了指数退避的概念。当多个线程试图同时获取锁时,就会发生争用。高争用意味着有许多这样的线程,低争用则相反。
在 TTASLock 类中,lock() 方法分两步:重复读取锁,当锁看起来可用时,调用 getAndSet(true) 尝试
超级会员免费看
订阅专栏 解锁全文
5万+

被折叠的 条评论
为什么被折叠?



