2011-08-26 17 views
2

我分拆一個新的線程,從數據庫中讀取數據。每X記錄一個標誌被髮信號通知主線程將在那裏處理已被檢索的記錄並保留一些並丟棄其他記錄。當標誌被髮信號時,我使用鎖來允許讀者線程等待處理器線程完成處理記錄。然而,看起來鎖沒有這樣做,因爲當我遍歷記錄時,更多的不斷增加(表明讀者線程仍在讀取)。這會導致集合被修改,導致InvalidOperationExecption。C#鎖定不允許關鍵代碼運行正常

也許我誤會了的「鎖定」的宗旨是或者我沒有正確使用它。下面是我有什麼僞:

readonly object locker = new object(); 
Dictionary screened = new Dictionary; 

Search(){ 
    Thread reader = new Thread(() => Read("search terms")); 
    reader.Start(); 

    while(found < desiredAmount){ 
     if(SIGNAL){ 
      lock(locker){ 
       ProcessRecords(); 
      } 
     } 
    } 
} 

Read(){ 
    Connect to DB 
    while(reader.Read()){ 
     screened.add(record); 
    } 
} 

ProcessRecords(){ 
    foreach(var x in screened){ 
     //process record 
    } 
} 

我希望僞是不夠好,從我的理解閱讀()不應該在鎖塊執行一段時間。請幫我理解鎖好一點。

PS是的,我已經閱讀鎖MSDNs文章,還是不太懂如何在更復雜的情況下使用鎖。

回答

4

您還需要在while循環中加鎖。如果有兩個或多個線程競爭同一個鎖的鎖會的工作,你上面的例子中,你有沒有競爭,因爲沒有其他線程從第一分開請求鎖。

Read(){ 
    Connect to DB 
     while(reader.Read()){ 
      lock(locker) 
       screened.add(record); 
     } 

} 

更好的方法是將鎖放入ProcessRecords()中。

+0

「更好」在那裏有點規範。我喜歡我的鎖控制精細,以避免瓶頸;還有一些同步對象不支持'遞歸鎖定'(reentrancy)。我相信在.NET中,只有新的ReaderWriterLock具有該屬性 – sehe

+0

我在發佈sehes回答的評論之後閱讀了此權限。這似乎現在正在工作。 – spots

1

你需要,如果你想他們是互相排斥的兩個線程在同一對象上鎖定。

因此,在較高的水平,要重複下列步驟,直到所有的記錄都處理:

  • 獲取鎖在你讀線程
  • 讀一些記錄
  • 獲取鎖在主螺紋
  • 過程記錄
+0

所以我應該在Read方法中鎖定被篩選的對象,然後當該標誌被髮信號時,在該方法中「解鎖」,然後鎖定Process()方法? msdn上的文章沒有提到這樣的事情。此外,我不明白我將如何鎖定某些內容,將該代碼塊鎖定在其他位置,然後再回到該位置。 – spots

+0

您不需要明確「解鎖」。當鎖結構內部的邏輯完成時,會自動發生。只要確保每個線程中的關鍵部分都被相同的鎖定對象包圍。 – newdayrising

+0

我不確定你想要完成什麼,但你可能希望讀者線程無限循環,並且在循環內部,邏輯將獲得鎖,然後從db讀取n條記錄(然後隱式釋放鎖)。 – newdayrising

1

我不知道我理解你的代碼...但如果你使用一個字典來實體店+讀取數據,那麼我強烈建議使用ConcurrentDictionary - 它是線程安全的,真快(因爲大多數操作實現無鎖)...

有關信息,請參閱:

+0

不會TryAdd()最終不會在ProcessRecords()方法遍歷它時添加記錄嗎? – spots

+0

是和否 - 請閱讀我答案中的最後一個鏈接... – Yahia

2

screened.Add()是不受保護的,AFAICT

嘗試添加像這樣的鎖:

while(reader.Read()){ 
    lock(locker) 
    { 
     screened.add(record); 
    } 
} 
+0

啊,我現在明白了。我像你說的那樣添加了鎖,但也必須在循環之前在ProcessRecords()方法中添加一個鎖。似乎現在正在工作。 – spots

+0

除非在示例中顯示它的_other_位置調用ProcessRecords,否則不應該需要第二個鎖。 – sehe

1

目前的情況是,在使用一個集合,如果你把任何被接受的條目時,該處理將重新處理重複它們。

一個更好的方法將是一個​​,可能使用框架4.0的BlockingCollection<T>

生產者/消費者隊列的總體思路是,數據庫讀者會把物品放入隊列和處理會從隊列中刪除他們,如果不停,把它們放在不同的集合。

0

通常,鎖被用來保護數據不被多個線程同時訪問。

需要訪問數據的代碼的每個部分必須首先獲取該鎖。

一種更好的方式做你正在嘗試做的將是你的線程之間傳遞消息。

比方說,雙方你的線程(閱讀器和搜索)每個具有存取權限相同System.Collections.Concurrent.ConcurentQueue。一旦您的閱讀器具有所需數量的行,它會將隊列中的對象或任何其他對象集合在一起。

你的搜索線程試圖獲取項目從使用TryDeque功能的隊列。當它無法獲得物品時,它會睡覺,當它可以獲得物品時,它會對其進行處理。

把隊列較大的組行可能會獲得更好的性能,因爲你的線程不會花很多時間試圖獲取鎖。