簡體   English   中英

自旋鎖vs std :: mutex :: try_lock

[英]Spinlock vs std::mutex::try_lock

使用專門設計的自旋鎖(例如http://anki3d.org/spinlock )與如下代碼相比有什么好處:

std::mutex m;
while (!m.try_lock()) {}
# do work
m.unlock();

在典型的硬件上,有很多好處:

  1. 您幼稚的“假自旋鎖”可能會在CPU旋轉時使內部CPU總線飽和,從而使其他物理核心(包括持有該鎖的物理核心)餓死。

  2. 如果CPU支持超線程或類似的功能,那么您幼稚的“假自旋鎖”可能會消耗物理內核上過多的執行資源,從而使共享該物理內核的另一個線程餓死。

  3. 天真的“假自旋鎖”可能會執行多余的寫操作,從而導致不良的緩存行為。 當您在x86 / x86_64 CPU上執行讀-修改-寫操作時(例如try_lock可能執行的比較/交換),即使值未更改,它也始終會寫入。 此寫操作導致高速緩存行在其他內核上無效,要求它們在另一個內核訪問該行時重新共享它。 如果其他內核上的線程同時爭用同一把鎖,這將很糟糕。

  4. 您幼稚的“假自旋鎖”與分支預測交互不良。 當您最終獲得鎖定時,您將所有錯誤預測的分支的父對象恰好鎖定了其他線程,並需要盡快執行。 這就像一個跑步者都准備好在起跑線上奔跑,但是當他聽到起跑手槍時,他停下來屏住呼吸。

基本上,該代碼會做所有可能導致自旋鎖發生錯誤的錯誤。 絕對沒有有效地做任何事情。 編寫良好的同步原語需要深厚的硬件專業知識。

使用自旋鎖的主要好處是,如果最重要的前提條件為true,則獲取和釋放該鎖的成本非常低: 鎖很少或沒有擁塞

如果您有足夠的把握知道不會發生爭用,那么自旋鎖的性能將大大優於單純的互斥量實現,后者將通過庫代碼來執行不必要的驗證,並執行系統調用。 這意味着要進行上下文切換(消耗數百個周期),並放棄線程的時間片,並導致重新安排線程的時間。 這可能會花費不確定的時間-即使鎖幾乎在此之后立即可用,您仍然必須等待幾十毫秒,然后線程才能在不利的條件下再次運行。

但是,如果沒有爭用的先決條件不成立,那么自旋鎖通常會因為沒有進展而大大遜色,但是它仍然像執行工作一樣消耗CPU資源。 在互斥鎖上進行阻塞時,您的線程不會消耗CPU資源,因此可以將這些資源用於其他線程來工作,否則CPU可能會關閉,從而節省了電源。 對於自旋鎖,這是不可能的,自旋鎖在成功(或失敗)之前一直在進行“主動工作”。
在最壞的情況下,如果等待者的數量大於CPU內核的數量,則自旋鎖可能會導致巨大的 ,不成比例的性能影響,因為處於活動狀態且正在運行的線程正在等待,而這種狀態在它們運行時永遠不會發生(因為釋放鎖需要運行其他線程!)。

另一方面,人們應該期望std::mutex每一個現代的不吸毒的實現都已經包含了一個微小的自旋鎖,而后退到進行系統調用。 但是...雖然這是一個合理的假設,但不能保證。

使用自旋鎖來支持std::mutex另一個非技術性原因可能是許可條款。 許可條款對於設計決策來說是一個很差的理由,但是它們可能還是很真實的。
例如,當前的GCC實現僅基於pthread,這意味着使用標准線程庫中的任何內容的“任何MinGW”都必須與winpthreads鏈接(缺少替代方法)。 這意味着您必須遵守winpthreads許可證,這意味着您必須復制其版權信息。 對於某些人來說,這是一個大問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM