2009-08-19 17 views
2

我有一個線程控制檯應用程序工作正常,但它的架構需要改進,我想要一些反饋。C#鎖定和一般線程設計問題

目前,程序加載一個數據列表,並將數據分割成分區(每個線程一個塊)。然後,程序使用ThreadPool初始化一個新線程,並將其傳遞給要運行的一部分分區數據。

一切都很好地工作...除了:

一些線程失敗......由於網絡問題,或無法恢復的異常。這是預期的行爲,而不是一個錯誤。

我現在需要一種方法(如果線程失敗)恢復該線程的數據段並將其提供給另一個工作線程以使其不成爲孤立。我確信有辦法做到這一點,即在線程之間共享數據等,但我認爲有一個更好的方法。

而不是事先分割數據並將其傳遞給每個線程,我可以在所有線程之間共享此數據的一個靜態集合。這更優雅,但引入了舊方法不必擔心的新同步問題。答案:

A.)你對這種方法和舊方法有什麼想法?
B.)如果這種方法很好,我該如何去鎖定對共享靜態集合的訪問。

當線程進入時,我可以鎖定集合並彈出一段僅用於該線程的數據。現在,該靜態集合將被該線程彈出的數量減少。在線程失敗後,我可以通過再次鎖定數據段將數據段重新分配給共享集合,並將數據重新推送到集合中供其他線程嘗試處理。

例如:(未經測試的僞代碼)

void Process(object threadInfo) 
{ 
    lock(StaticCollection) 
    { 
    var segment = StaticCollection.Take(100); 
    StaticCollection.Remove(StaticCollection.Where(item => segment.Contains(item))) 
    } 

    foreach(var seg in segment) 
    { 
    // do something 
    } 

    // reallocate the thread's data on failure 
    if(unrecoverableErrorOccurred) 
    { 
    lock(StaticCollection) 
    { 
     StaticCollection.Add(segment); 
    } 
    } 
} 

我在正確的軌道與此有關?在我看來,一個線程可以在另一個線程重新分配項目的同時移除項目...或者對STATIC集合執行鎖定意味着根本沒有其他線程可以訪問該集合。因此,線程A.)在方法的第一部分獲得了一個鎖,它會阻止所有其他線程執行方法的最後部分,直到完成ThreadA爲止。

+0

您的解決方案實際上非常好(除了撥打和撥打電話,您可能需要考慮使用實際的堆棧)。我喜歡你提供的解決方案,因爲它也可以讓你只將已經失敗的對象推回堆棧或隊列,並且不需要其他收集來跟蹤每個項目的處理狀態。 現在有人可能會尖叫,爲什麼這是可怕的... – LorenVS 2009-08-19 19:47:09

+0

操作是否需要成功或失敗作爲一個單一的交易? (並且這個過程是否改變了它所在的部分的數據?) – 2009-08-19 20:06:23

+0

這兩個數字都不是。 – Scott 2009-08-19 21:05:01

回答

4

讓我們分離出來,這裏的幾件事情...

首先,你不是實際上鎖定集合。您正在鎖定與對象關聯的顯示器。我個人認爲這是一個錯誤,.NET遵循Java給每個對象一個關聯的監視器來鎖定,但讓我們把它留在一邊。我個人更喜歡有對象和相關變量純粹鎖定 - 所以在我的代碼,你可能會看到:

private readonly object padlock = new object(); 

這確保了沒有其他代碼將嘗試獲得該鎖的,因爲他們贏得不知道對象。

其次,鎖是諮詢。這是「你沒有鎖定收藏品」的一部分。「如果集合本身在同一個鎖上同步 - 而非泛型集合爲此目的有一個Synchronized方法 - 但基本上除非某處某處明確地取出一個鎖,否則將不會同步

第三,是的,代碼中顯示的兩個鎖定塊是使用相同的鎖(當然,假設StaticCollection的值不會改變)如果一個線程正忙於呼叫Remove,那麼將阻止任何其他線程在調用Add at同一時間,因爲他們每個人都需要有鎖。這可能是你想要的。

我個人不會認爲它是真的靜態雖然收集(或者說,我不會使用StaticCollection變量)。我會給每個任務一個對同一個集合的引用(以及對相關鎖的引用;事實上,我可能會封裝集合,同步和「讓我做一堆工作」,「這裏有一些工作要做在一個單獨的類中有「位」)。這將使測試變得更簡單,而且邏輯上通常更好。這也意味着你可以有兩個獨立的「集合」線程同時在不同的集合上工作......如果你將上面的封裝通用化,那麼它們可能會執行完全不同的任務......

+0

非常感謝您的見解。 – Scott 2009-08-19 22:19:08

0

您可能會考慮使用Queue來保存未處理的塊,正如Jon Skeet所說,鎖定一箇中性對象,並且只保留足夠長的時間來訪問Queue。我用很多線程使用了這種方法,對我來說效果很好。