继续Task <T>时保留异常

dtb*_*dtb 10 .net c# task-parallel-library

我有一个Task <T>:

Task<A> someTask = ...
Run Code Online (Sandbox Code Playgroud)

此任务可能导致成功,出现故障或取消.

我希望在任务成功时转换结果,如果没有则保留结果.

someTask抛出异常时,这似乎非常困难.

我尝试过的:

Task<B> resultTask = StartMyTask().ContinueWith<B>(
    t => Foo(t.Result),
    TaskContinuationOptions.OnlyOnRanToCompletion);
Run Code Online (Sandbox Code Playgroud)

resultTask如果出现someTask故障,则会导致取消.我想要它的错.

Task<B> resultTask = StartMyTask().ContinueWith<B>(
    t => Foo(t.Result));
Run Code Online (Sandbox Code Playgroud)

这会破坏Visual Studio调试器,因为.Result会引发异常.如果按F5,resultTask按预期出现故障,但它闻起来有气味.

有没有什么办法让resultTask具有相同的结果一样someTask,如果someTask故障?


基本上我要做的是用任务来表达这样的事情:

int F()
{
    throw new SomeException();
}

string G(int x)
{
    return x.ToString();
}

try
{
    string result = G(F());
}
catch (SomeException e)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 5

我怀疑原来的异常会在你的AggregateException内部,AggregateException如果你看到我的意思 - 你只需要打开两次,或者AggregateException.Flatten()在外面打电话AggregateException.


dtb*_*dtb 5

这似乎可行,并且可能类似于@Stephen Cleary建议的“ PipelineTransform” 。

Task<B> resultTask = StartMyTask().ContinueWith<Task<B>>(task =>
{
    var tcs = new TaskCompletionSource<B>();

    switch (task.Status)
    {
    case TaskStatus.Canceled:
        tcs.SetCanceled();
        break;

    case TaskStatus.Faulted:
        tcs.SetException(task.Exception);
        break;

    case TaskStatus.RanToCompletion:
        tcs.SetResult(Foo(task.Result));
        break;
    }

    return tcs.Task;
}).Unwrap();
Run Code Online (Sandbox Code Playgroud)


Ste*_*ary 4

任务延续是独立的。它们可用于构建您想要的内容,但它们并不是专门针对该场景而设计的。

要问的第一个问题是:这种关系是否可以被视为父/子关系(例如,Foo将是 的父级StartMyTask)?如果这是有道理的,那么您也许可以利用从子级到父级的状态传播。

如果将Foo“父母”和StartMyTask“孩子”分别对待在设计上不起作用,那么就没有其他选择了。对于您所需要的内容来说,延续性有点低级(请记住,它们只是“在其他任务完成时运行此任务”)。

听起来您可能正在尝试做一些更像“管道”的事情。目前来说,Rx比较适合这种事情。

基于任务的管道还没有真正出现。ParallelExtensionsExtras有一个基于任务的 Pipeline 类,Async CTP有一个 TPL Dataflow 库,但这两个库目前都尚未开发。(例如,Pipeline 坚持在单独的线程中运行管道的每个阶段,而 Dataflow 没有自动传播异常甚至完成的机制)。

因此,如果您无法使用 Rx,那么我会为任务编写自己的“PipelineTransform”扩展方法,并使用显式 TCS 来正确处理所有三种完成情况。