2016-02-28 16 views
9

私はTPLとタスクライブラリの文書をカバーしてカバーしています。しかし、私はまだ以下のケースを非常にはっきりと理解することができず、今は実装する必要があります。IOバウンド操作の並列実行

私は自分の状況を簡略化します。私はIEnumerable<Uri>の長さ1000を持っています。HttpClientを使ってそれらの要求をしなければなりません。

私には2つの質問があります。

  1. だけでHTTPリクエストを待って、あまり計算があります。この場合も引き続きParallel.Foreach()を使用できますか?
  2. 代わりにTaskを使用する場合は、膨大な数を作成するのがベストプラクティスですか?私がTask.Factory.StartNew()を使用し、それらのタスクをリストに追加し、それらのすべてを待つとしましょう。最大タスク数を制御し、最大でHttpClientを作成できる機能(TPLパーティショナーなど)はありますか?

はSO上で同様の質問がいくつかありますが、誰が最大値に言及していません。要件は、最大HttpClientで最大タスクを使用することです。

は、事前にありがとうございます。

答えて

11

この場合でもまだParallel.Foreachを使用できますか?

これは実際には適切ではありません。 Parallel.Foreachは、CPU集約的な作業のためのものです。また、非同期操作もサポートしていません。

Taskを使用する場合は、膨大な数を作成するのがベストプラクティスですか?

代わりにTPLデータフローブロックを使用してください。あなたはスレッドが利用可能になるのを待ってそこに座っている膨大な量のタスクを作成しません。タスクの最大量を設定し、その間にタスクを待っているバッファ内にあるすべてのアイテムに対してそれらを再利用することができます。たとえば:TPLデータフローと

var block = new ActionBlock<Uri>(
    uri => SendRequestAsync(uri), 
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 50 }); 

foreach (var uri in uris) 
{ 
    block.Post(uri); 
} 

block.Complete(); 
await block.Completion; 
+0

? – ozgur

+0

@ozgurその制限がどこで設定されているかによって異なります。しかし、ある場合は、MaxDegreeOfParallelismをそれより低い値に設定してください。 – i3arnon

+0

最後の質問。あなたが提供した例はIO操作に適していますが、CPUの並列処理は必要ありませんか? – ozgur

12

i3arnonの答えは良いです。データフローは、特にCPUとI/Oバウンドコードが混在している場合に便利です。私はParallelがCPUバウンドコード用に設計されているという彼の感想をエコーし​​ます。 I/Oベースのコードには最適なソリューションではなく、特には非同期コードには適していません。

あなたはほとんど-I/Oのコードでうまく動作の代替ソリューションをしたい場合は - と外部ライブラリを必要としない - あなたが探している方法はTask.WhenAllです:

var tasks = uris.Select(uri => SendRequestAsync(uri)).ToArray(); 
await Task.WhenAll(tasks); 

これは、最も簡単なソリューションですが、すべての要求を同時に開始するという欠点があります。特に、すべての要求が同じサービス(または少数のサービス)に向かう場合、タイムアウトが発生する可能性があります。これを解決するには、何らかのスロットルを使用する必要があります...

私は作成できる最大タスク数と最大HttpClientの数を制御する機能(TPLパーティショナーなど)はありますか?

TPL Dataflowには、一度に多くの数だけ開始するnice MaxDegreeOfParallelismがあります。代わりに、タスクを使用する場合には

private readonly SemaphoreSlim _sem = new SemaphoreSlim(50); 
private async Task SendRequestAsync(Uri uri) 
{ 
    await _sem.WaitAsync(); 
    try 
    { 
    ... 
    } 
    finally 
    { 
    _sem.Release(); 
    } 
} 

、それらの膨大な数を作成するためのベストプラクティスは何です:あなたはまた、別の組み込み、SemaphoreSlimを使用することにより、通常の非同期コードを絞ることができますか? Task.Factory.StartNew()を使用してこれらのタスクをリストに追加し、すべてのタスクを待機するとしましょう。

実際にはStartNewは使用しないでください。非常にまれな1つの適切なユースケース(動的タスクベースの並列処理)しかありません。バックグラウンドスレッドに作業をプッシュする必要がある場合は、最新のコードでTask.Runを使用する必要があります。しかし、あなたはそれが最初に必要なことさえないので、StartNewでもTask.Runもここでは適切ではありません。

SOについても同様の質問がいくつかありますが、最大値は何も記載されていません。要件は、最大HttpClientで最大タスクを使用することです。非同期コードが本当にトリッキー取得する場所

最大値があります。 CPUバインド(パラレル)コードでは、解決策は明らかです。コアを持つスレッド数を使用します。 (まあ、少なくともあなたはそこを開始し、必要に応じて調整することができます)。非同期コードでは、ソリューションのように明白ではありません。あなたが持っているどのくらいのメモリ、リモートサーバーがどのように応答するか(レート制限、タイムアウトなど)など

ここでは簡単な解決策はありません - それは多くの要因に依存します。特定のアプリケーションが並行性の高いレベルをどのように処理しているかをテストし、次にいくつかの低い数値に絞り込むだけです。


私は異なる技術が適切であるときに説明しようとするいくつかの slides for a talk持って(並列処理、非同期、TPLデータフローを、およびRx)。レシピで書かれた説明のほうが好きな人は、 my bookから同時性について恩恵を受けると思います。

同時要求の数を作ることができますhttpリクエスト・オペレーティング・システムの最大数を超えた場合はどうすれば
+2

簡単な解決法はないと言ったとき、それは私の苦痛を終わらせました。おそらくそれを行う方法があると思っていて、昼と夜を探していました。今私は自分の状況に特有のものを実装しようとすることができます。どうもありがとうございました。 – ozgur