我總是感到困惑。有人會解釋什麼Reentrant意味着在不同的情況下?你爲什麼要使用可重入和不可重入?什麼是重入鎖和概念?
說pthread(posix)鎖定原語,它們是否可重入?使用它們時應該避免哪些陷阱?
互斥體是否可重入?
我總是感到困惑。有人會解釋什麼Reentrant意味着在不同的情況下?你爲什麼要使用可重入和不可重入?什麼是重入鎖和概念?
說pthread(posix)鎖定原語,它們是否可重入?使用它們時應該避免哪些陷阱?
互斥體是否可重入?
重入鎖定
一個可重入鎖是其中一個進程可以鎖定多次權利要求而不阻塞在其自身上。在跟蹤您是否已經抓住鎖的情況下很有用。如果鎖不可重入,您可以抓住鎖,然後在再次抓住鎖時阻止,從而使自己的流程陷入僵局。
一般而言,重入是代碼的一個屬性,它沒有中央可變狀態,如果代碼在執行時被調用,可能會被破壞。這樣的調用可以由另一個線程完成,也可以通過源自代碼本身的執行路徑遞歸地進行。
如果代碼依賴於可以在執行過程中更新的共享狀態,它不會重入,至少在更新可能會破壞它的情況下不會重入。
一個用例重入鎖定
A(有些通用的,做作),例如,對於重入鎖的應用程序可能是:
你有一些計算涉及一種遍歷圖的算法(可能有循環)。由於循環或由於到相同節點的多條路徑,遍歷可能會多次訪問同一節點。
數據結構受到併發訪問的限制,可能由於某種原因更新,可能是由另一個線程更新的。您需要能夠鎖定各個節點以處理由於競爭條件而導致的潛在數據損壞。出於某種原因(也許性能),您不希望全局鎖定整個數據結構。
您的計算無法保留您訪問過哪些節點的完整信息,或者您正在使用的數據結構不允許'我以前在這裏'的問題需要快速回答。
這種情況的一個例子是Dijkstra算法的簡單實現,該算法將優先級隊列實現爲二進制堆或使用簡單鏈表作爲隊列的廣度優先搜索。在這些情況下,掃描隊列中現有的插入是O(N),您可能不希望在每次迭代時都這樣做。
在這種情況下,跟蹤您已獲取的鎖是非常昂貴的。假設您想要在節點級別執行鎖定,則重入鎖定機制可以減輕您是否曾經訪問節點的需要。您可以盲目地鎖定節點,也許在將它從隊列中彈出後解鎖。
重入互斥
一個簡單的互斥是不可重入作爲只有一個線程可以是在給定時間的臨界區。如果你抓住互斥鎖然後嘗試再次抓住它,一個簡單的互斥鎖就沒有足夠的信息來告訴誰以前持有它。要遞歸執行此操作,您需要一種機制,其中每個線程都有一個令牌,以便您可以分辨誰已經獲取了互斥鎖。這使得互斥體機制更加昂貴,所以你可能不想在所有情況下都這樣做。
IIRC POSIX線程API確實提供了可重入和不可重入互斥選項。
重入鎖讓你寫一個方法M
這給資源A
鎖,然後調用M
遞歸或從已經持有A
鎖碼。
使用非重入鎖定,您需要2個版本的M
,一個鎖定,一個不鎖定,另外一個邏輯可以調用正確的鎖定。
這裏的圖片說明會很有幫助... – Rachel 2013-08-20 18:29:53
這個tutorial中的折返鎖很好地被描述。
本教程中的示例遠不及關於遍歷圖的答案。在非常簡單的情況下,重入鎖定非常有用。
雖然這種情況通常應該避免,因爲它也很難避免死鎖等。無論如何,線程已經夠難了,而不必懷疑你是否已經鎖定了。 – 2009-08-21 14:28:33
+1,還要考慮鎖定不可重入的情況,如果你不小心,可以阻止自己。此外,在C語言中,您沒有與其他語言一樣的機制來確保鎖定被釋放多次,因爲它被收購。這可能會導致很大的問題。 – user7116 2009-08-21 14:30:50
這就是昨天發生在我身上的事情:我沒有將再次進入的問題納入考慮範圍,並最終調試了5個小時的僵局...... – vehomzzz 2009-08-21 14:34:55