是WhenAll顺序还是并发?

时间:2013-10-02 16:29:07

标签: c# async-await

每次下面的操作都会抓住Mike的异常。

当所有顺序涉及每个任务之间的延续上下文或者所有任务是否同时运行时?如果它同时发生,为什么迈克的例外总是被抓住而不是米奇的。我拖延迈克,以便给米奇一个机会。如果它是顺序的,那使它并发?在进行Web请求/执行文件处理时是否会应用并发执行?

假设这个代码更严重,那么这是一种合理的异步方法吗?场景将是几种方法 - 杰森,米奇和迈克 - 并发运行而没有阻塞,并在完成后继续事件处理程序?我应该注意哪些关于我天真实现异常处理的注意事项?有任何问题或潜在的问题需要注意吗?

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");
    }
}

1 个答案:

答案 0 :(得分:7)

  

WhenAll是顺序还是并发?

这个问题并不适用。所有基础任务完成后,WhenAll的任务即告完成。如何做到这一点就是它的业务。

当涉及到例外时,Exception的{​​{1}}属性包含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; } } 作为我们自己的版本,只是稍微区别地处理异常。另一个选择是根据它们将完成的顺序对任务进行排序,我们可以有趣地做,同时仍然保持异步并且对任务一无所知。这是一个WhenAll方法,它接受一系列任务并返回表示相同操作的一系列任务,但是根据完成时间排序(按升序排列)。

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); } ,为我们提供的每个任务添加一个延续,然后当任何任务完成时,我们将尚未完成的TaskCompletionSource标记为刚刚完成的任务的结果是。

使用这个我们现在可以写:

TaskCompletionSource

,异常将是首先抛出的异常。