2011-07-03 65 views
2

我有一些工作(工作)在隊列中(所以有幾個),我希望每個工作都由一個線程處理。需要線程處理的作業隊列

我在看Rx,但這不是我想要的,然後遇到並行任務庫。

由於我的工作將在一個Web應用程序,我不希望客戶端在等待各項工作完成完成,所以我也做了以下內容:

public void FromWebClientRequest(int[] ids); 
    { 
     // I will get the objects for the ids from a repository using a container (UNITY) 


     ThreadPool.QueueUserWorkItem(delegate 
             { 
              DoSomeWorkInParallel(ids, container); 
             }); 
    } 

    private static void DoSomeWorkInParallel(int[] ids, container) 
    { 
     Parallel.ForEach(ids, id=> 
             { 
              Some work will be done here... 
              var respository = container.Resolve... 
             }); 


     // Here all the work will be done. 
     container.Resolve<ILogger>().Log("finished all work"); 
    } 

我將在調用上面的代碼一個Web請求,然後客戶端不必等待。

這是正確的方法嗎?

TIA

+2

我知道它只是僞代碼,但請告訴我你不會實例化你的倉庫(從IOC容器)在你的lambda內.. –

+0

不,它已經存在,它是一個單身人士。 –

回答

3

從MSDN文檔我看到Unitys的IContainer解決方法不是線程安全的(或不寫)。這意味着你需要從線程循環中完成。編輯:改爲Task

public void FromWebClientRequest(int[] ids); 
{ 
    IRepoType repoType = container.Resolve<IRepoType>(); 
    ILogger logger = container.Resolve<ILogger>(); 
    // remove LongRunning if your operations are not blocking (Ie. read file or download file long running queries etc) 
    // prefer fairness is here to try to complete first the requests that came first, so client are more likely to be able to be served "first come, first served" in case of high CPU use with lot of requests 
    Task.Factory.StartNew(() => DoSomeWorkInParallel(ids, repoType, logger), TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness); 
} 

private static void DoSomeWorkInParallel(int[] ids, IRepoType repository, ILogger logger) 
{ 
    // if there are blocking operations inside this loop you ought to convert it to tasks with LongRunning 
    // why this? to force more threads as usually would be used to run the loop, and try to saturate cpu use, which would be doing nothing most of the time 
    // beware of doing this if you work on a non clustered database, since you can saturate it and have a bottleneck there, you should try and see how it handles your workload 
    Parallel.ForEach(ids, id=>{ 
        // Some work will be done here... 
        // use repository 
      }); 
    logger.Log("finished all work"); 
} 

另外,正如fiver所述,如果你有.Net 4,那麼任務就是要走的路。

爲什麼去工作(在評論的問題):

如果你的方法fromClientRequest會被瘋狂往往被炒了,你將填補線程池,和整體系統性能可能不會像在.NET 4中有良好細粒化。這是任務進入遊戲的地方。每個任務不是它自己的線程,但是新的.Net 4線程池創建足夠的線程來最大化系統性能,並且不需要打擾多少個cpus和多少線程上下文切換。

一些MSDN行情線程池:

當所有線程池中的線程已經 分配給任務,線程池 不會立即開始創建新 空閒線程。爲避免 不必要地爲線程分配堆棧空間 ,它會間隔創建新的空閒線程 。間隔爲 ,目前爲半秒,但它在 .NET Framework的未來版本中可能會發生更改 。

線程池具有的每間可用 處理器

250工作線程默認大小增加不必要的 空閒線程數也可能導致 性能問題。堆棧空間必須爲每個線程分配 。如果太多 許多任務同時開始,它們的所有 可能看起來很慢。 尋找合適的餘額是 性能調整問題。

通過使用任務,您可以放棄這些問題。

另一件好事是你可以細化運行類型的操作。如果您的任務運行阻止操作,這一點很重要。這是一個更多線程同時分配的情況,因爲他們大多會等待。線程池不能自動地實現這一點:

Task.Factory.StartNew(() => DoSomeWork(), TaskCreationOptions.LongRunning); 

當然,你可以讓它完成需求,而不訴諸ManualResetEvent的:

var task = Task.Factory.StartNew(() => DoSomeWork()); 
task.Wait(); 

除了這個你不必改變Parallel.ForEach如果您不希望出現異常或阻止,因爲它是.Net 4任務並行庫的一部分,並且(通常)可以很好地在.NET 4池上運行並進行優化,就像任務一樣。

但是,如果您確實轉到Tasks而不是parallel for,請從調用程序Task中刪除LongRunning,因爲Parallel.For是阻止操作,啓動任務(包含fiver循環)不是。但是,這種方式可能會導致先到先得的優化失敗,或者您必須在更多的任務(全部通過ID生成)上執行這些任務,否則這些任務可能會導致更少的正確行爲。另一種選擇是等待DoSomeWorkInParallel結尾的所有任務。

+0

謝謝你,沒有意識到容器不是線程安全的。 –

+0

爲什麼要完成任務? –

+0

在編輯中添加了回覆 –

3

另一種方法是使用任務:

public static void FromWebClientRequest(int[] ids) 
{ 
    foreach (var id in ids) 
    { 
     Task.Factory.StartNew(i => 
     { 
      Wl(i); 
     } 
     , id); 
    } 
} 
+0

但是現在您必須處理每項工作中的例外情況。 –

+0

@亨克:我的每份工作都處理可能會出現的任何異常情況。那麼這種模式會很好用嗎?說到這一點,我喜歡Parallel.ForEach()的語法和Task一起使用。 –

1

我會叫上的Web 請求,上面的代碼,然後客戶端將不會 等待。

這將工作,只要客戶端不需要答案(如確定/失敗)。

這是正確的 方式做到這一點?

差不多。您可以使用Parallel.ForEach(TPL)作業,但可以從「普通」的Threadpool作業運行它。更好地使用外部工作的任務。

另外,處理該外部任務中的所有異常。並注意容器的線程安全等

+0

感謝您的意見。我仍然在學習如何在這種情況下最好地使用線程。爲什麼使用Task來代替(異常處理)會更好? –

+1

TPL調度程序可以在主作業等待時重新使用該線程。在其他情況下,您還可以獲得更好的集成。 –

+0

下面的其他人提到使用Task.Factory.StartNew()。這會更好,而不是使用Parallel.ForEach()? –