-1

經過this question with the same title及其答案後,我想嘗試一些應該只使用臨界區域才能真正起作用的東西,因此速度應該快於現有的解決方案(也使用其他內核對象像互斥或信號)只使用關鍵部分的讀/寫鎖會導致死鎖

這裏是我的讀/寫鎖定/解鎖功能:

#include <windows.h> 

typedef struct _RW_LOCK 
{ 
    CRITICAL_SECTION readerCountLock; 
    CRITICAL_SECTION writerLock; 
    int readerCount; 
} RW_LOCK, *PRW_LOCK; 

void InitLock(PRW_LOCK rwlock) 
{ 
    InitializeCriticalSection(&rwlock->readerCountLock); 
    InitializeCriticalSection(&rwlock->writerLock); 
} 

void ReadLock(PRW_LOCK rwlock) 
{ 
    EnterCriticalSection(&rwlock->readerCountLock); // In deadlock 1 thread waits here (see description below) 
    if (++rwlock->readerCount == 1) 
    { 
     EnterCriticalSection(&rwlock->writerLock); // In deadlock 1 thread waits here 
    } 
    LeaveCriticalSection(&rwlock->readerCountLock); 
} 

void ReadUnlock(PRW_LOCK rwlock) 
{ 
    EnterCriticalSection(&rwlock->readerCountLock); 
    if (--rwlock->readerCount == 0) 
    { 
     LeaveCriticalSection(&rwlock->writerLock); 
    } 
    LeaveCriticalSection(&rwlock->readerCountLock); 
} 

int WriteLock(PRW_LOCK rwlock) 
{ 
    EnterCriticalSection(&rwlock->writerLock); // In deadlock 3 threads wait here 
} 

void WriteUnlock(PRW_LOCK rwlock) 
{ 
    LeaveCriticalSection(&rwlock->writerLock); 
} 

這裏是一個線程函數。在從main呼叫InitLock (&g_rwLock);之後,我創建了五個線程來嘗試這些鎖。

void thread_function() 
{ 
    static int value = 0; 
    RW_LOCK g_rwLock; 

    while(1) 
    { 
     ReadLock(&g_rwlLock); 
     BOOL bIsValueOdd = value % 2; 
     ReadUnlock(&g_rwlLock); 

     WriteLock(&g_rwlLock); 
     value ++; 
     WriteUnlock(&g_rwlLock); 
    } 
} 

理想情況下,此代碼應該保持運行而不會有任何問題。但令我失望的是,它並沒有運行總是。有時它陷入僵局。我編譯並在Windows XP上運行它。要使用線程池創建線程,我正在使用第三方庫。因此,在這裏不能給出所有涉及大量初始化例程和其他內容的代碼。

但是爲了簡化故事,我想知道是否有人通過查看上面的代碼可以指出這種方法有什麼問題?

我已經在上面的代碼中註釋了每個線程(出於五個線程)在死鎖發生時一直等待。 (我發現通過附加調試器到死鎖進程)

任何輸入/建議將是非常好的,因爲我堅持了很長一段時間現在(在使我的代碼比以往更快運行的貪婪) 。

+0

請注意,這是一個讀者優先鎖定 - 如果您的讀者數量從未達到0,寫入將永遠等待。如果這是不可取的,你可能想要考慮一個票務系統,如[Mellor-Crummey-Scott](http://www.cs.rochester.edu/u/scott/papers/1991_PPoPP_read_write.pdf)「公平「的鎖定。 – 2014-12-01 20:10:58

+0

與往常一樣:_RW_LOCK是爲編譯器保留的名稱(前導下劃線後跟大寫字母) – 2014-12-01 20:11:12

+1

請發佈演示此問題的*實際*代碼。這裏的代碼顯然已經在翻譯中失去了一些東西,正在浪費有用的人的時間。 – 2014-12-01 21:03:07

回答

4

斑點至今兩件事情:

  • 您初始化每個線程,這是不允許的關鍵部分(行爲是不確定的)
  • 從不同的線程不能離開一個關鍵部分即進入它的人(「如果一個線程調用LeaveCriticalSection,當它沒有指定的臨界區對象的所有權,發生錯誤時可以使用EnterCriticalSection無限期地等待引起另一個線程。」)

後者符合你看到的僵局。

一旦您同時有多個閱讀器,您不能控制他們調用ReadUnlock的順序,所以您不能確保第一個線程(允許調用LeaveCriticalSection的唯一一個線程)是最後一個線程。

+0

注意每個線程都有不同的關鍵部分。很明顯,這不是他想要的,但它意味着'LeaveCriticalSection'在一個單獨的線程不會造成死鎖。 – 2014-12-01 20:23:01

+0

@Cory:哦,好吧,發現了。在這種情況下,什麼都不應該阻止,並且「價值」上存在競爭條件。 UB每個標準,但不會在Windows上出現這些症狀。 – 2014-12-01 20:50:48

+0

其實,想一想,什麼是'價值'?與「價值」不一樣。所以我現在不知道我們可以信任的代碼,以及我們不能。如果實際代碼中實際上只有一個關鍵部分,也許它只是一次,但我的第二點仍然適用。 – 2014-12-01 21:03:10

0

這種方式無法正確運行。

  • 允許1個線程進入ReadLock(),讓它通過++指令,但進入作家CS
  • 另一個線程進入WriteLock()和successfuly進入writerCS

所以現在我們之前暫停讀者數= 1,並同時運行作者。請注意,讀卡器在EnterCriticalSection上是死鎖的(& rwlock-> writerLock)

+0

這意味着讀者將被封鎖,直到作者離開關鍵部分。但是,提問者觀察到的僵局是什麼阻止了作者? – 2014-12-01 20:15:50

+0

也許你是對的,我錯過了解釋計數器。 – Anonymous 2014-12-01 20:22:41