2012-06-14 143 views
6

它們是如何實現的,特別是在pthreads的情況下。他們在引擎蓋下使用什麼pthread同步API?一些僞代碼將不勝感激。如何在pthread中實現讀/寫鎖?

+7

也許你可以閱讀代碼? – tbert

+1

也許[this](http://www.cognitus。淨/ html/howto/pthreadSemiFAQ_10.html)可以提供幫助嗎? –

回答

8

我沒有做過編程一會兒任何並行線程,但是當我這樣做,我從來沒有使用POSIX讀/寫鎖。問題是大多數時候互斥體就足夠了:即。你的關鍵部分很小,而且該地區的表現並不那麼重要,以至於雙重障礙值得擔心。

在這種情況下,性能是一個問題,通常使用原子操作(通常作爲一個編譯器擴展)是一個更好的選擇(即額外的障礙是問題,而不是臨界區的大小)。

通過你消除所有這些情況的時候,你留下,你必須需要一個真正的RW鎖定特定的性能/公平/ RW偏置要求的情況;那就是當你發現POSIX rw-lock的所有相關性能/公平參數都是未定義的和實現特定的。在這一點上,你通常會更好地實施你自己的,所以你可以確保滿足適當的公平/ rw-bias要求。

的基本算法是保持多少每個都在臨界區的計數,如果一個線程不允許訪問的是,分流其關閉,以一個合適的隊列中等待。你們的大部分努力都是在實施服務兩隊之間的適當公平性/偏見。

下面的C類並行線程般的僞代碼說明了什麼,我想說的。

struct rwlock { 
    mutex admin; // used to serialize access to other admin fields, NOT the critical section. 
    int count; // threads in critical section +ve for readers, -ve for writers. 
    fifoDequeue dequeue; // acts like a cond_var with fifo behaviour and both append and prepend operations. 
    void *data; // represents the data covered by the critical section. 
} 

void read(struct rwlock *rw, void (*readAction)(void *)) { 
    lock(rw->admin); 
    if (rw->count < 0) { 
    append(rw->dequeue, rw->admin); 
    } 
    while (rw->count < 0) { 
    prepend(rw->dequeue, rw->admin); // Used to avoid starvation. 
    } 
    rw->count++; 
    // Wake the new head of the dequeue, which may be a reader. 
    // If it is a writer it will put itself back on the head of the queue and wait for us to exit. 
    signal(rw->dequeue); 
    unlock(rw->admin); 

    readAction(rw->data); 

    lock(rw->admin); 
    rw->count--; 
    signal(rw->dequeue); // Wake the new head of the dequeue, which is probably a writer. 
    unlock(rw->admin); 
} 

void write(struct rwlock *rw, void *(*writeAction)(void *)) { 
    lock(rw->admin); 
    if (rw->count != 0) { 
    append(rw->dequeue, rw->admin); 
    } 
    while (rw->count != 0) { 
    prepend(rw->dequeue, rw->admin); 
    } 
    rw->count--; 
    // As we only allow one writer in at a time, we don't bother signaling here. 
    unlock(rw->admin); 

    // NOTE: This is the critical section, but it is not covered by the mutex! 
    //  The critical section is rather, covered by the rw-lock itself. 
    rw->data = writeAction(rw->data); 

    lock(rw->admin); 
    rw->count++; 
    signal(rw->dequeue); 
    unlock(rw->admin); 
} 

類似上面的代碼是任何rwlock實現的起點。仔細考慮一下你的問題的本質,並用適當的邏輯代替出列,以確定下一步應該喚醒哪一類線程。根據應用情況,允許有限數量/期限的讀者跨越作家或反過來是很常見的。

當然,我的一般偏好是完全避免rw-locks;通常通過使用原子操作,互斥鎖,STM,消息傳遞和持久數據結構的某種組合。然而,有些時候你真正需要的是一個rw-lock,而當你這樣做的時候,知道它們是如何工作是很有用的,所以我希望這有助於它。

編輯 - 爲響應(很合理)的問題,我在哪裏等待上面的僞代碼:

我已經假定出隊實現包含的等待,讓內某處append(dequeue, mutex)prepend(dequeue, mutex)有是一段代碼:

while(!readyToLeaveQueue()) { 
    wait(dequeue->cond_var, mutex); 
} 

這就是爲什麼我在相關互斥體中傳遞給隊列操作的原因。

+0

你能指出一個關於爲什麼以及如何避免RW鎖的更詳細的解釋嗎?我一直在我認爲不應該的地方使用pthread RW鎖,並想更多地瞭解您所談論的內容。 – puffadder

+0

這不是關於避免RW鎖。正如我在回答中所說的,當你需要一個RW鎖時,你應該使用一個。問題在於,很少需要RW鎖,也不需要偏見/公平性要求。 Posix RW鎖沒有任何偏見/公平保證,因此在您可能想要使用它們的時候,它們具有諷刺意味的不可移植性。鑑於實現您自己的便攜式RW鎖並不難,您也可以。 – Recurse

+0

你在哪裏等待代碼中的信號。我在幾個地方看到了信號(rw-> deque),但是沒有代碼要等待那個信號。 – pythonic

0

每一個實現可以是不同的,但通常他們都贊成在默認情況下由於POSIX的要求讀者一個線程能夠獲得上rwlock中多次讀鎖。如果他們喜歡編寫者,那麼只要編寫者等待,讀者就會在第二次讀取鎖定嘗試時死鎖,除非實現可以確定讀者已經擁有讀鎖定,但唯一確定的方法是存儲所有線程的列表它們擁有讀鎖,這在時間和空間要求上效率非常低。

+1

您的評論僅適用於重入rw-lock。正如我的回答所證明的,非重入鎖只需要一個計數。而且,列表是實施重入的數據結構的一個很差的選擇。短陣列,位圖和線性哈希表的緩存意識組合可以使簿記更有效地利用空間/時間。 – Recurse

+0

通過折返你是否意味着遞歸?重入是比遞歸鎖定更強的要求。 –