Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我想等待抛出AggregateException,而不仅仅是第一个异常_C#_Task Parallel Library_C# 5.0_Async Await - Fatal编程技术网

C# 我想等待抛出AggregateException,而不仅仅是第一个异常

C# 我想等待抛出AggregateException,而不仅仅是第一个异常,c#,task-parallel-library,c#-5.0,async-await,C#,Task Parallel Library,C# 5.0,Async Await,等待出现故障的任务(设置了异常的任务)时,wait将重新显示存储的异常。如果存储的异常是一个aggregateeexception,它将重新调用第一个异常并放弃其余异常 如何使用wait同时抛出原始的aggregateeexception,以免意外丢失错误信息 请注意,当然可以想出一些黑客解决方案(例如,尝试绕过Wait,然后调用Task.Wait)。我真的希望找到一个干净的解决办法这里的最佳做法是什么? 我曾想过使用定制的等待器,但内置的TaskAwaiter包含了很多魔力,我不知道如何完全重

等待出现故障的任务(设置了异常的任务)时,
wait
将重新显示存储的异常。如果存储的异常是一个
aggregateeexception
,它将重新调用第一个异常并放弃其余异常

如何使用
wait
同时抛出原始的
aggregateeexception
,以免意外丢失错误信息

请注意,当然可以想出一些黑客解决方案(例如,尝试绕过
Wait
,然后调用
Task.Wait
)。我真的希望找到一个干净的解决办法这里的最佳做法是什么?

我曾想过使用定制的等待器,但内置的
TaskAwaiter
包含了很多魔力,我不知道如何完全重现。它在TPL类型上调用内部API。我也不想重复所有这些

如果你想玩它,这里有一个简短的复制:

static void Main()
{
    Run().Wait();
}

static async Task Run()
{
    Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
    await Task.WhenAll(tasks);
}

static Task CreateTask(string message)
{
    return Task.Factory.StartNew(() => { throw new Exception(message); });
}
Run
中只抛出两个异常中的一个

请注意,有关堆栈溢出的其他问题并没有解决此特定问题。建议复制时请小心。

我可以说得更多,但那只是虚张声势。玩玩它,它确实像他们说的那样有效。你只要小心就行了

也许你想要这个

(就我个人而言,我不愿等待,但这只是我的偏好)

回应评论(评论回复太长)

然后使用线程作为你的出发点,作为一个类似的论点,作为最佳实践,这里将有一个来源

除非实现代码将异常传递出去,否则异常很容易被吞没(例如,wait正在预先包装的异步模式……在引发事件时将它们添加到event args对象)。当您有一个场景,您启动任意数量的线程并在其上执行时,您无法控制每个线程的顺序或终止点。此外,如果一个错误与另一个错误相关,则永远不会使用此模式。因此,您强烈地暗示rest的执行是完全独立的——也就是说,您强烈地暗示这些线程上的异常已经作为异常处理。如果您想在这些线程中处理异常以外的其他事情,您应该将它们添加到通过引用传入的锁定集合中-您不再将异常视为异常,而是将其视为一条信息-使用并发包,将异常包装在您需要的信息中,以识别它来自的上下文,该上下文可能会被传递到它中


不要混淆您的用例。

我不同意您的问题标题中暗示的
await
的行为是不受欢迎的。这在绝大多数情况下都是有意义的。在
whalll
情况下,您真正需要了解所有错误详细信息(而不是一个)的频率有多高

AggregateException
的主要困难在于异常处理,也就是说,您失去了捕获特定类型的能力

也就是说,您可以通过扩展方法获得所需的行为:

public static async Task WithAggregateException(this Task source)
{
  try
  {
    await source.ConfigureAwait(false);
  }
  catch
  {
    // source.Exception may be null if the task was canceled.
    if (source.Exception == null)
      throw;

    // EDI preserves the original exception's stack trace, if any.
    ExceptionDispatchInfo.Capture(source.Exception).Throw();
  }
}
public static async Task WithAggregateException(this Task source)
{
    try { await source.ConfigureAwait(false); }
    catch (OperationCanceledException) when (source.IsCanceled) { throw; }
    catch { source.Wait(); }
}

public static async Task<T> WithAggregateException<T>(this Task<T> source)
{
    try { return await source.ConfigureAwait(false); }
    catch (OperationCanceledException) when (source.IsCanceled) { throw; }
    catch { return source.Result; }
}

我知道我迟到了,但我发现了这个巧妙的小把戏,它能满足你的需要。由于等待的任务上有完整的异常集,因此调用此任务的等待或.Result将引发聚合异常

    static void Main(string[] args)
    {
        var task = Run();
        task.Wait();
    }
    public static async Task Run()
    {

        Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
        var compositeTask = Task.WhenAll(tasks);
        try
        {
            await compositeTask.ContinueWith((antecedant) => { }, TaskContinuationOptions.ExecuteSynchronously);
            compositeTask.Wait();
        }
        catch (AggregateException aex)
        {
            foreach (var ex in aex.InnerExceptions)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    static Task CreateTask(string message)
    {
        return Task.Factory.StartNew(() => { throw new Exception(message); });
    }

我不想为了捕捉我所期望的异常而放弃练习。这使我想到以下扩展方法:

public static async Task WithAggregateException(this Task source)
{
  try
  {
    await source.ConfigureAwait(false);
  }
  catch
  {
    // source.Exception may be null if the task was canceled.
    if (source.Exception == null)
      throw;

    // EDI preserves the original exception's stack trace, if any.
    ExceptionDispatchInfo.Capture(source.Exception).Throw();
  }
}
public static async Task WithAggregateException(this Task source)
{
    try { await source.ConfigureAwait(false); }
    catch (OperationCanceledException) when (source.IsCanceled) { throw; }
    catch { source.Wait(); }
}

public static async Task<T> WithAggregateException<T>(this Task<T> source)
{
    try { return await source.ConfigureAwait(false); }
    catch (OperationCanceledException) when (source.IsCanceled) { throw; }
    catch { return source.Result; }
}
公共静态异步任务NoSwallow(此任务),其中TException:Exception{
试一试{
等待任务;
}捕获(特克斯例外){
var unexpectedEx=task.Exception
.flatte()
.内部例外情况
.FirstOrDefault(ex=>!(ex是TException));
如果(意外事件!=null){
抛出新的NotImplementedException(null,unexpectedEx);
}否则{
抛出任务异常;
}
}
}
消费代码可以如下所示:

试试看{
等待Task.WhenAll(tasks.NoSwallow();
捕获(聚合异常){
手部异常(ex);
}

bone Head异常将具有与同步世界中相同的效果,即使它是偶然与
MyException
同时抛出的。使用
NotImplementedException
包装有助于不丢失原始堆栈跟踪。

下面是Stephen Cleary扩展方法的一个较短实现:

public static async Task WithAggregateException(this Task source)
{
  try
  {
    await source.ConfigureAwait(false);
  }
  catch
  {
    // source.Exception may be null if the task was canceled.
    if (source.Exception == null)
      throw;

    // EDI preserves the original exception's stack trace, if any.
    ExceptionDispatchInfo.Capture(source.Exception).Throw();
  }
}
public static async Task WithAggregateException(this Task source)
{
    try { await source.ConfigureAwait(false); }
    catch (OperationCanceledException) when (source.IsCanceled) { throw; }
    catch { source.Wait(); }
}

public static async Task<T> WithAggregateException<T>(this Task<T> source)
{
    try { return await source.ConfigureAwait(false); }
    catch (OperationCanceledException) when (source.IsCanceled) { throw; }
    catch { return source.Result; }
}
带有AggregateException的公共静态异步任务(此任务源)
{
请尝试{await source.ConfigureAwait(false);}
当(source.IsCanceled){throw;}时捕获(OperationCanceledException)
捕获{source.Wait();}
}
带有AggregateException的公共静态异步任务(此任务源)
{
尝试{return await source.ConfigureAwait(false);}
当(source.IsCanceled){throw;}时捕获(OperationCanceledException)
catch{return source.Result;}
}
此方法基于Stephen Toub在GitHub的API提案中提出的建议



更新:我添加了对取消案例的特殊处理,以防止传播包含
操作取消异常的
聚合异常
的尴尬。现在直接传播
操作取消异常
,并保留其状态。值得称赞的是@noseratio for pointin在answer的注释中找出这个缺陷。当然,现在这个实现并不比Stephen Cleary的方法短多少!

扩展,它包装了原始的聚合异常,并且不改变返回类型,因此它仍然可以与
任务一起使用

公共静态任务UnswallowExceptions(此任务t)
=>t.ContinueWith(t=>t.IsFaulted?抛出