3

以下が実行されるたびに、Mike の例外がキャッチされます。

各タスク間の継続コンテキストを含む WhenAll シーケンシャルですか、それともすべてのタスクが同時に実行されますか? 同時の場合、Mitch ではなく、Mike の例外が常にキャッチされるのはなぜですか。ミッチにチャンスを与えるために、私はマイクを遅らせた. シーケンシャルである場合、それを並行にするために何が必要ですか? Web リクエスト/ファイル処理を行うときに同時実行が適用されますか?

このコードがより深刻であると仮定すると、これは非同期に対する賢明なアプローチでしょうか? シナリオは、いくつかのメソッド (Jason、Mitch、Mike) であり、ブロックせずに同時に実行し、すべてが完了したらイベント ハンドラーを続行しますか? 例外処理の単純な実装に関して、どのような考慮事項に注意する必要がありますか? 注意すべき問題または潜在的な問題はありますか?

private async void button1_Click(object sender,EventArgs e)
{
    try
    {
        AsyncJason c1 = new AsyncJason();
        await c1.Hello();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

public class AsyncJason
{
    public AsyncJason()
    {
    }

    public async Task Hello()
    {
        var j = await GetJasonAsync();
        string[] dankeSchon = await Task.WhenAll(new Task<string>[] {GetJasonAsync(), GetMikeAsync(), GetMitchAsync()});
    }

    private async Task<string> GetJasonAsync()
    {
        var result = await Task.Run<string>(() => GetJason());
        return result;
    }

    private string GetJason()
    {
        return "Jason";
    }

     private async Task<string> GetMitchAsync()
    {
        var result = await Task.Run<string>(() => GetMitch());
        return result;
    }

    private string GetMitch()
    {
        throw new ArgumentException("Mitch is an idiot", "none");
    }

     private async Task<string> GetMikeAsync()
    {
        await Task.Delay(3000);
        var result = await Task.Run<string>(() => GetMike());
        return result;
    }

    private string GetMike()
    {
        throw new ArgumentException("Mike is an idiot", "none");
    }
}
4

1 に答える 1

8

WhenAll はシーケンシャルですか、コンカレントですか?

質問は実際には当てはまりません。のタスクWhenAllは、基礎となるすべてのタスクが完了すると完了します。それをどのように行うかは、そのビジネスです。

例外に関して言えば、 のExceptionプロパティには、基礎となるすべてのタスクによってスローされたすべての例外Task含まれてAggregateExceptionいます。

await複数の例外を表す集計例外を持つタスクの場合、そのリスト内のすべての例外ではなく、そのリストの最初の例外をラップ解除して再スローします。AggregateException

それを作成するときAggregateException(明らかに、これがどこでも保証されているかどうかはわかりません)は、WhenAllそれらのタスクが完了した順序ではなく、に渡されたタスクの順序に基づいて例外をリストします。

失われた例外が心配な場合は、返されたタスクを保存して、すべての例外を調べるか、単にラップされたを再スローできるようにする必要がありますAggregateException

public async Task Hello()
{
    var j = await GetJasonAsync();
    var task = Task.WhenAll(new Task<string>[] { GetJasonAsync(), GetMikeAsync(), GetMitchAsync() });
    try
    {
        string[] dankeSchon = await task;
    }
    catch (Exception)
    {
        throw task.Exception;
    }
}

最初にヒットした例外が本当に必要な場合は、実行可能な再スローされた例外になります。WhenAll1 つのオプションは、基本的に、例外をわずかに異なる方法で処理する独自のバージョンに書き直すことです。もう 1 つのオプションは、タスクが完了する順序に基づいてタスクを順序付けすることです。これは、興味深いことに、非同期性を維持し、タスクについて何も知らなくても実行できます。次のOrderメソッドは、一連のタスクを受け取り、同じ操作を表す一連のタスクを返しますが、完了時間に基づいて (昇順で) 順序付けされています。

public static IEnumerable<Task<T>> Order<T>(this IEnumerable<Task<T>> tasks)
{
    var taskList = tasks.ToList();

    var taskSources = new BlockingCollection<TaskCompletionSource<T>>();

    var taskSourceList = new List<TaskCompletionSource<T>>(taskList.Count);
    foreach (var task in taskList)
    {
        var newSource = new TaskCompletionSource<T>();
        taskSources.Add(newSource);
        taskSourceList.Add(newSource);

        task.ContinueWith(t =>
        {
            var source = taskSources.Take();

            if (t.IsCanceled)
                source.TrySetCanceled();
            else if (t.IsFaulted)
                source.TrySetException(t.Exception.InnerExceptions);
            else if (t.IsCompleted)
                source.TrySetResult(t.Result);
        }, CancellationToken.None, TaskContinuationOptions.PreferFairness, TaskScheduler.Default);
    }

    return taskSourceList.Select(tcs => tcs.Task);
}

基本的にここでの考え方は、TaskCompletionSourcefor each タスクを作成し、提供された各タスクに継続を追加し、任意のタスクが完了したら、まだ完了していない TaskCompletionSource をマークして、完了したばかりのタスクの結果を示すことです。それは。

これを使用して、次のように記述できます。

public async Task Hello()
{
    var j = await GetJasonAsync();
    var tasks = new[] { GetJasonAsync(), GetMikeAsync(), GetMitchAsync() };
    string[] dankeSchon = await Task.WhenAll(tasks.Order());
}

例外は、最初にスローされた例外になります。

于 2013-10-02T16:40:00.787 に答える