2014-03-13 94 views
2

根據這個問題here使用pthread_spin_lock對於鎖定關鍵部分是危險的,因爲線程可能會被調度程序中斷而中斷,並且該資源上的其他線程內容可能會左旋轉。搶佔,pthread_spin_lock和原子內置

假設我決定從pthread_spin_lock切換到通過原子內置+ compare_and_swap idion實現的鎖定:這件事會改善還是會受到這個問題的困擾?

因爲使用pthread它似乎沒有什麼禁用搶佔,有什麼我可以做的情況下,我使用通過原子實現的鎖或任何我可以看看?

我想鎖定一個小的關鍵區域。

+0

'pthread_spin_lock'最可能是用原子操作實現的,你想實現什麼?如果你想在關鍵部分執行*的內容只是更新/增量或類似的東西,你肯定應該使用原子構建(或C11'_Atomic')來執行操作並根本不鎖定。 –

+0

我想用pthread_spin_lock鎖定關鍵部分。根據我插入的URL,看起來存在其他線程無限期旋轉的風險。我的問題是,如果我通過使用compare_and_swap + atomic來執行關鍵區域的鎖定,會發生什麼情況...是否有其他線程可能因搶佔而無限期旋轉? –

+0

@Jens:我再補充一些解釋。我需要鎖定一小段關鍵部分。 –

回答

3

pthread_mutex_lock通常具有使用原子操作嘗試獲取鎖的快速路徑。如果鎖沒有,這可能非常快。只有當鎖已經被鎖定,線程纔會通過系統調用進入內核。內核獲取一個自旋鎖,然後重新嘗試獲取互斥鎖,以防第一次嘗試釋放該互斥鎖。如果此嘗試失敗,則將調用線程添加到與該互斥鎖關聯的等待隊列中,並執行上下文切換。內核還在互斥體中設置了一些位,以表明有一個等待線程。

pthread_mutex_unlock也有一個快速路徑。如果等待的線程標誌清除,它可以簡單地釋放鎖。如果該標誌被設置,線程必須通過系統調用進入內核,這樣等待的線程才能被喚醒。同樣,內核必須獲得自旋鎖,以便它可以操縱其線程控制數據結構。如果沒有線程在等待,鎖可以由內核釋放。如果有一個線程在等待,它就會運行,並且互斥量的所有權被轉移而不被釋放。

這個小舞蹈中有許多微妙的競賽條件,希望它能正常工作。

由於嘗試獲取鎖定互斥鎖的線程已切換到上下文環境,因此它不會阻止擁有互斥鎖的線程運行,從而使所有者有機會退出它的臨界區並釋放互斥鎖。

相比之下,嘗試獲取鎖定自旋鎖的線程只是旋轉,消耗CPU週期。這有可能阻止擁有旋轉鎖的線程退出其臨界區並釋放鎖。旋轉的線程在其時間片已被佔用時可被搶佔,從而允許擁有該鎖的線程最終重新獲得控制權。當然,這對於性能並不好。

實際上,使用自旋鎖的地方不會在擁有鎖的情況下搶佔線程。內核可以設置一個per-cpu標誌來防止它從中斷服務程序執行上下文切換(或者它可以提高中斷優先級以防止可能導致上下文切換的中斷,或者它可能完全禁止中斷)。用戶線程可以通過提高優先級來阻止自己被搶佔(由同一進程中的其他線程搶佔)。請注意,在單處理器系統中,防止當前線程被搶佔,從而不需要旋轉鎖定。或者,在多處理器系統中,可以將線程綁定到cpus(cpu affinity),以便它們不會搶佔彼此。所有的鎖最終都需要一個原子基元(很好,高效的鎖;參見here的一個反例)。如果互斥體高度競爭,互斥鎖可能效率低下,導致線程不斷進入內核並進行上下文切換;特別是如果臨界區小於內核開銷。自旋鎖可以更有效率,但只有當擁有者不能被搶佔並且關鍵部分很短時。請注意,當線程嘗試獲取鎖定的互斥鎖時,內核仍然必須獲取旋轉鎖定。

就我個人而言,我會使用原子操作來處理共享計數器更新和互斥鎖等更復雜的操作。只有在分析之後,我纔會考慮用自旋鎖代替互斥體(並找出如何處理搶佔)。請注意,如果您打算使用condvars,則別無選擇,只能使用互斥鎖。

+0

哇...這是一個外國人!非常感謝這個大流量的信息。非常感謝這篇大文章! –