2013-01-18 98 views
3

這是在Linux上,但理想情況下,我想要一個POSIX兼容的解決方案。有什麼辦法可以鎖定互斥鎖,並讓它在給定時間後自動解鎖?

我有兩個線程,每個線程都可以通過API函數寫入硬件總線。在任何給定的時間,只有一個線程被允許寫入總線,並且在總線寫入之後,總線不能寫入數百毫秒量級的給定時間。

這兩個線程都是作爲狀態機實現的。

顯而易見的解決方案是使用互斥鎖來串行訪問總線,設置計數器或時間戳,並讓狀態機在適當的時間到期後解鎖互斥鎖。

這對我來說似乎有點脆弱。如果未來的維護開發人員錯誤地修改狀態機,則該互斥體可能無法釋放,從而導致難以診斷的死鎖。

是否有一個API調用會鎖定互斥鎖,但會在給定的超時後自動解鎖它?谷歌揭示pthread_mutex_timedlock(),但這不完全相同的事情。

+1

設置計時器,在到期時解鎖互斥鎖。 – mah

+1

@mah:你不能從一個信號處理程序調用'pthread_mutex_unlock',可以嗎?那麼我們在談論什麼類型的計時器,以及它何時有機會在持有鎖的線程上下文中運行? Linux至少有'SIGEV_THREAD_ID',它可以讓你在正確的線程中得到一個信號,你可以用它設置一個標誌來告訴線程釋放互斥量。但線程將不得不定期檢查標誌。 –

+0

@SteveJessop我在pthread_mutex_unlock的手冊頁中看不到它在信號處理程序中不能使用的明確指示,儘管我在那裏看到的內容可能不明確 - 表明在處理完信號後,線程將繼續等待互斥體信號沒有發生。我不知道這是否意味着即使互斥鎖已解鎖,線程也不會注意到(儘管我希望不會!)。我認爲解決這個問題的一個非常難看的方法是讓信號處理程序啓動一個新線程,其唯一目的是解鎖互斥鎖。 – mah

回答

3

我不知道任何允許鎖自動釋放的pthreads API。

就我個人而言,我會嘗試通過單線程串行訪問總線以避免這些複雜情況。如果該線程維護一個輸入隊列並且其他線程將請求發送給它,那麼它可以執行任何序列化和衝突解決方案,這也使得在進一步訪問之前實現延遲更容易。只要你小心不要在這個線程中使用任何阻塞函數,你唯一真正的失敗就是它可能會被意外終止。

如果你需要從總線上讀寫,你可以給每個線程一個輸入隊列,讓工作線程向IO線程發送一個「讀請求」,然後等待響應被髮回給它自己的輸入隊列。如果只有一個未完成的線程請求,那麼你並不需要一個隊列,一個簡單的condition variable和一個指向結構填充的指針可能會正常工作。

如果你確實想要兩個線程共享資源,那麼我認爲你總會有互斥鎖被鎖定的風險。即使您設置了一個計時器並在固定時間後強制釋放互斥鎖,這可能會導致不同的錯誤,因爲線程繼續使用資源時認爲它有鎖,而實際上它完全由另一個線程持有。最終,你正在試圖規劃健壯性與未來的編程錯誤,這是一個很好的目標,但有一個限制 - 互斥體只是一個人必須小心。

我建議你最好的辦法,如果你必須去互斥路由,就是簡單地最小化需要互斥鎖的代碼,並避免阻止它內部的調用。如果您正在實現一個狀態機,請嘗試確保互斥鎖不必在狀態轉換之間保持鎖定狀態。如果可能的話,請確定互斥鎖的範圍,以便它們僅在您的調用鏈中的某個級別的單個函數調用期間保持不變 - 這樣可以更輕鬆地找出鎖定/解鎖失配。如果你使用的是C++,那麼使用RAII來釋放你的鎖更可靠。

但是,我認爲你會發現生活通過聲明一個線程仲裁者(現有線程或新線程之一)以某種方式輕鬆地序列化你的請求變得更容易。

+0

這對我來說似乎很好。在沒有時間限制的互斥體的情況下,總線訪問線程對我來說似乎是最簡單的解決方案。它符合POSIX標準。 –

+0

確實。通常最簡單的解決方案將導致最少的錯誤!如果您擔心公共線程隊列的爭用,可以嘗試使用無鎖隊列(如[liblfds](http://www.liblfds.org/)中的那些隊列),但是聽起來我覺得保護總線線程隊列的互斥量的開銷不會太大,而且無鎖實現可能是複雜的和/或錯誤的。如果你不想阻塞,你也可以使用回調函數來接收讀取結果,但如果你不小心的話,來自另一個線程的回調可能會變得稍微繁瑣。 – Cartroo

+1

@SimonElliott:我同意Cartroo的設計。總線隊列鎖定開銷是可以忽略的。通過讓調用者指定一個指向信號量('sem_t *')的指針和一個保存結果的指針,可以避免回調問題。總線訪問線程將保存結果並在操作完成時保存'sem_post()'信號量。然後調用者可以在信號量上「sem_wait()」或「sem_timedwait()」等待結果,或者使用sem_trywait()進行輪詢。請參閱'man sem_init','man sem_post'和'man sem_wait'(位於http://www.kernel.org/doc/man-pages/online/dir_all_alphabetic.html)。 –

1

如何在共享(和互斥鎖覆蓋的)內存中擁有最後一次總線訪問的時間戳?每個寫會比這個樣子:

  1. 鎖定互斥
  2. 的上次訪問
  3. 檢查時間戳;如果您的等待時間尚未,睡眠中度過的這段時間剩餘,因爲你不能做任何事情在這個線程反正
  4. 做寫的上次訪問
  5. 更新時間戳
  6. 解鎖互斥
+1

+1,儘管與提問者要求的內容有一個潛在的差異。在這段代碼中,無論在超時期間首先出現哪個線程都會獲得鎖定。如果互斥鎖只在時間到期時才被釋放,那麼當時優先級最高的服務員將獲得鎖定。如果所有競爭線程都具有相同的優先級,那麼這不是一個區別,無論如何,您可以修改這些代碼以更接近地請求所需內容。 –

1

如果遇到此問題,我可能會使用另一個'APIwrite'線程與驅動程序進行交互。這個線程將圍繞生產者 - 消費者隊列流行和睡眠(幾百毫秒)循環。任何其他希望寫入隊列的線程都會寫入* writeBuffer。 writeBuffer結構可以包含一個函數(* writeBuffer)指針,寫入後APIwrite線程將調用它。由始發線程提供的此功能可能會導致一些事件,即始發線程正在等待或可能只釋放* writeBuffer。

節省顯式互斥量,定時器等,並允許同步或異步寫入。