Parallel.ForEach
例外の処理に関しては、他のループと何ら変わりはありません。例外がスローされると、ループの処理が停止します。これがおそらく、パーセンテージに差異が見られる理由です(ループを処理しているときに、カウントを処理している可能性があると思います)。
また、クラスParallel.ForEach
で行っている非同期呼び出しはIO完了(ネットワーク応答)の待機をブロックするため、実際には必要ありません。それらは計算上バインドされていません(計算上バインドされている場合ははるかに優れています)。WebClient
Parallel.ForEach
そうは言っても、最初に呼び出しをに変換しWebClient
て使用する必要がありますTask<TResult>
。クラスを使用すると、イベントベースの非同期パターンをタスクベースの非同期パターンに簡単に変換できます。TaskCompletionSource<TResult>
Uri
の呼び出しの結果として生成される一連のインスタンスがあると仮定すると、getKey
これを行うための関数を作成できます。
static Task<String> DownloadStringAsync(Uri uri)
{
// Create a WebClient
var wc = new WebClient();
// Set up your web client.
// Create the TaskCompletionSource.
var tcs = new TaskCompletionSource<string>();
// Set the event handler on the web client.
wc.DownloadStringCompleted += (s, e) => {
// Dispose of the WebClient when done.
using (wc)
{
// Set the task completion source based on the
// event.
if (e.Cancelled)
{
// Set cancellation.
tcs.SetCancelled();
return;
}
// Exception?
if (e.Error != null)
{
// Set exception.
tcs.SetException(e.Error);
return;
}
// Set result.
tcs.SetResult(e.Result);
};
// Return the task.
return tcs.Task;
};
上記は、1つ WebClient
を使用するように最適化できることに注意してください。これは、演習として残されています(テストで必要であることが示されていると仮定します)。
そこから、次のシーケンスを取得できますTask<string>
。
// Gotten from myKeywords
IEnumerable<Uri> uris = ...;
// The tasks.
Task<string>[] tasks = uris.Select(DownloadStringAsync).ToArray();
タスクの実行を開始するには、 extensionメソッドを呼び出す必要があることに注意してください。これは、延期された実行を回避するためです。を呼び出す必要はありませんが、リスト全体を列挙してタスクの実行を開始するものを呼び出す必要があります。ToArray
ToArray
これらのインスタンスを取得したら、次のように、クラスのメソッドTask<string>
を呼び出すことで、すべてのインスタンスが完了するのを待つことができます。ContinueWhenAll<TAntecedentResult>
TaskFactory
Task.Factory.ContinueWhenAll(tasks, a => { }).Wait();
tasks
これが行われると、配列を循環し、Exception
および/またはResult
プロパティを調べて、例外または結果が何であったかを確認できます。
ユーザーインターフェイスを更新する場合は、Enumerable.Selectの呼び出しをインターセプトすることを検討する必要があります。つまり、ダウンロードが完了したときに、のContinueWith<TNewResult>
メソッドを呼び出して、次のTask<TResult>
ように操作を実行する必要があります。
// The tasks.
Task<string>[] tasks = uris.
Select(DownloadStringAsync).
// Select receives a Task<T> here, continue that.
Select(t => t.ContinueWith(t2 => {
// Do something here:
// - increment a count
// - fire an event
// - update the UI
// Note that you have to take care of synchronization here, so
// make sure to synchronize access to a count, or serialize calls
// to the UI thread appropriately with a SynchronizationContext.
...
// Return the result, this ensures that you'll have a Task<string>
// waiting.
return t2;
})).
ToArray();
これにより、発生したものを更新できます。上記の場合、Select
再度呼び出す場合はt2
、エラー処理メカニズムの状態に応じて、他のイベントの状態を確認して発生させることができます。