2011-07-03 3 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コンテナから)リポジトリをインスタンス化するつもりはないと教えてください。 –

+0

いいえ、それはすでに存在し、それはシングルトンでした。 –

答えて

3

。これはスレッドループからそれを行う必要があることを意味します。編集: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"); 
} 

また、あなたが.Net 4を持っている場合は、タスクが進む方法です。

なぜタスク(コメントで質問を)行く:

あなたの方法fromClientRequestがめちゃくちゃしばしば解雇されるだろう場合は、スレッドプールを埋めるだろう、とシステム全体のパフォーマンスは、おそらくによる.NET 4と同様に良好ではないでしょう細かい砂目立て。これはタスクがゲームに入る場所です。各タスクは独自のスレッドではありませんが、新しい.Net 4スレッドプールはシステム上のパフォーマンスを最大限にするのに十分なスレッドを作成し、CPUの数とスレッドコンテキストスイッチの量を気にする必要はありません。

いくつかのMSDNはThreadPoolのため引用:

すべてのスレッドプールのスレッドがタスクに を割り当てられている場合は、スレッドプール はすぐに 新しいアイドルスレッドの作成を開始しません。 がスレッドのためにスタック領域 を不必要に割り当てるのを避けるため、間隔を空けて新しいアイドル スレッドを作成します。現在、 は、 .NET Frameworkの将来のバージョンで変更される可能性がありますが、現在の間隔は です。

スレッドプールは、不必要にも パフォーマンス上の問題を引き起こす可能性があります アイドルスレッドの数を増やす可能な プロセッサ

あたり 250ワーカースレッドのデフォルトサイズを持っています。スタック空間は、各スレッドに対して を割り当てる必要があります。 多くのタスクが同時に開始される場合は、 のすべてが遅く表示されることがあります。 正しいバランスを見つけることは、 パフォーマンスチューニングの問題です。

タスクを使用することで、これらの問題を破棄します。

別の良いことは、実行する操作の種類を細かくすることができることです。タスクがブロック操作を実行する場合、これは重要です。これは、ほとんどのスレッドが同時に待機するため、より多くのスレッドを同時に割り当てる場合です。 ThreadPoolのは、自動的にこれを達成することはできません。

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

そしてもちろん、あなたはそれがManualResetEventに頼ることなく、オンデマンドで終える作ることができます。この他に

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

をあなたはParallel.ForEachを変更する必要はありません例外やブロックが予想されない場合は、.Net 4 Task Parallel Libraryの一部であり、(しばしば)正常に動作し、.Net 4プールで最適化されます。

ただし、ParallelではなくTaskに移動する場合、Parallel.Forはブロック操作であり、Startingタスク(fiverループあり)はLongRunningを呼び出し元タスクから削除します。しかし、このようにして先着順最適化を緩和するか、おそらくそれほど正しい動作を与えない多くのタスク(すべてのIDを通じて生成される)で実行する必要があります。別のオプションは、DoSomeWorkInParallelの最後にあるすべてのタスクを待機することです。

+0

ありがとう、コンテナがスレッドセーフではなかったことを認識していませんでした。 –

+0

なぜ仕事は行くのだろうか? –

+0

編集で追加されました –

3

もう一つの方法は、タスクを使用することです:私はUnitysいるIContainerの解決法は、(またはそれが書かれていません)スレッドセーフではないことがわかり、MSDNのドキュメントから

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

しかし、それぞれのジョブで例外を処理する必要があります。 –

+0

@Henk:私の仕事のそれぞれは、上昇する可能性のある例外を処理します。だから、このパターンはうまく使えますか?それを言って、Parallel.ForEach()の構文は、それをキックオフするTaskと一緒に好きです。 –

1

私は、Web リクエストに応じて上記のコードを呼ぶだろうし、クライアントは は待つ必要はありません。

これは、クライアントが(Ok/Failのような)回答を必要としない場合に機能します。

これは正しい方法ですか。 これを行うには?

ほぼ。ジョブに対してはParallel.ForEach(TPL)を使用しますが、「プレーン」スレッドプールジョブから実行します。外側の仕事にもタスクを使うのがよいでしょう。

また、その外部タスクですべての例外を処理します。

+0

ありがとうございます。私はこのシナリオでスレッドを最大限に活用する方法をまだ学んでいます。代わりにタスク(例外処理)を使用する方が良い理由は何ですか? –

+1

TPLスケジューラは、待機中にメインジョブからスレッドを再利用できます。他のシナリオでは、統合性も向上します。 –

+0

以下は、Task.Factory.StartNew()を使用して説明しました。 Parallel.ForEach()を使用する代わりに、これを使用する方が良いでしょうか? –

関連する問題