2011-09-08 38 views
2

該類用於獲取URL列表,掃描它們,然後返回那些不起作用的列表。它使用多個線程來避免在長列表上永遠佔用。併發硬盤故障<T> in .net c#4

我的問題是,即使我用一個測試函數替換所有url上實際掃描的URL,該類會返回可變數量的失敗。

我假設我的問題在於ConcurrentStack.TryPop()或.Push(),但我不知道爲什麼我的生活。他們被認爲是線程安全的,我也嘗試過鎖定,沒有幫助。

任何人都可以向我解釋我做錯了什麼?我沒有很多的多線程經驗..

public class UrlValidator 
{ 
    private const int MAX_THREADS = 10; 
    private List<Thread> threads = new List<Thread>(); 
    private ConcurrentStack<string> errors = new ConcurrentStack<string>(); 
    private ConcurrentStack<string> queue = new ConcurrentStack<string>(); 

    public UrlValidator(List<string> urls) 
    { 
     queue.PushRange(urls.ToArray<string>()); 
    } 

    public List<string> Start() 
    { 
     threads = new List<Thread>(); 

     while (threads.Count < MAX_THREADS && queue.Count > 0) 
     { 
      var t = new Thread(new ThreadStart(UrlWorker)); 
      threads.Add(t); 
      t.Start(); 
     } 

     while (queue.Count > 0) Thread.Sleep(1000); 

     int runningThreads = 0; 
     while (runningThreads > 0) 
     { 
      runningThreads = 0; 
      foreach (Thread t in threads) if (t.ThreadState == ThreadState.Running) runningThreads++; 
      Thread.Sleep(100); 
     } 

     return errors.ToList<string>(); 
    } 

    private void UrlWorker() 
    { 
     while (queue.Count > 0) 
     { 
      try 
      { 
       string url = ""; 
       if (!queue.TryPop(out url)) continue; 
       if (TestFunc(url) != 200) errors.Push(url); 
      } 
      catch 
      { 
       break; 
      } 
     } 
    } 

    private int TestFunc(string url) 
    { 
     Thread.Sleep(new Random().Next(100)); 
     return -1; 
    } 
} 
+0

'ConcurrentStack queue' - 你不使用'ConcurrentQueue',而是有一個名爲'queue'堆什麼特別的原因? – AakashM

+0

呵呵,不是真的。我只是快速地閱讀併發命名空間,並知道堆棧的行爲,所以我認爲這是出於熟悉我猜。它應該仍然工作。 – Toodleey

回答

1

您的問題無關ConcurrentStack,而是與你在哪裏檢查正在運行的線程循環:

int runningThreads = 0; 
while (runningThreads > 0) 
{ 
    ... 
} 

的條件是立即假的,所以你從來沒有真正等待的線程。反過來,這意味着errors將包含迄今爲止運行的任何線程的錯誤。

但是,您的代碼有其他問題,但手動創建線程可能是最大的問題。由於您使用的是.NET 4.0,因此您應該使用任務或PLINQ進行異步處理。使用PLINQ,您的驗證可以實現爲:

public IEnumerable<string> Validate(IEnumerable<string> urls) 
{ 
    return urls.AsParallel().Where(url => TestFunc(url) != 200); 
} 
+0

我的噢我的,我不覺得愚蠢!感謝您發現,我一直在看代碼。我也會嘗試PLINQ實現。再次感謝! – Toodleey

2

這是值得的Task Parallel LibraryPLINQ(並行LINQ)將在真正的好。檢查出多少,如果你讓.NET做它的東西更容易的事情將是一個例子:

public IEnumerable<string> ProcessURLs(IEnumerable<string> URLs) 
{ 
    return URLs.AsParallel() 
     .WithDegreeOfParallelism(10) 
     .Where(url => testURL(url)); 
} 

private bool testURL(string URL) 
{ 
    // some logic to determine true/false 
    return false; 
} 

只要有可能,你應該讓.NET提供需要做的任何線索管理庫。 TPL一般來說非常適合這種情況,但由於您只是簡單地轉換單個物品,PLINQ非常適合這一點。您可以修改並行度(我建議將其設置爲小於併發TCP連接的最大數量),並且可以像LINQ允許的那樣添加多個條件。自動並行運行,並且不會執行線程管理。