有没有一种方法可以等待TPL任务而不抛出异常?

14

我们中的一些人更喜欢以少量异常的方式编码。然而,如果您等待一个任务并行库(Task Parallel Library)任务,并且该任务抛出了异常,它也会在调用线程上抛出异常。是否有一种(最好是标准的)方法可以避免这种行为,只需在收到响应时检查异常即可?


基本上你是在问,如果某段代码抛出了异常,有没有办法在不捕获它的情况下检测到它? - Andrew Savinykh
你确定你绝对需要“等待”任务吗?我建议在等待调用之后使用.ContinueWith(...)来完成你的操作。 - Jens Kloster
2
这是一个应该使用continuations来查询先前任务状态的实例。Continuations对于TPL非常重要,避免对它们进行黑客攻击。更重要的是,吞掉异常是一种代码坏味道。 - Gusdor
5个回答

20
您可以使用 Task.WaitAny,例如:
        var task = Task.Run(() =>
        {
            // ...
            throw new Exception("Blah");
        });
        Task.WaitAny(task);
        if (task.IsFaulted)
        {
            var error = task.Exception;
            // ...
        }
        else if (task.IsCanceled)
        {
            // ...
        }
        else
        {
            // Success
        }

9

如果没有引发异常,您无法等待失败的任务。但是,您可以等待该任务的后续任务,该任务仅在原始任务完成而未引发异常时完成:

public static Task SwallowExceptions(this Task task)
{
    return task.ContinueWith(_ => { });
}

faultedTask.SwallowExceptions().Wait();
if (faultedTask.IsFaulted)
{
    // handle exception
}

如果您的任务返回一个值,您可以在扩展方法中表示它并返回实际的值,如果没有异常,则返回该值或者如果有异常则返回默认值:
public static Task<T> SwallowExceptions<T>(this Task<T> task)
{
    return task.ContinueWith(completedTask => 
        completedTask.IsFaulted 
            ? default(T) 
            : completedTask.Result);
}

8

很遗憾,这个功能不是内置的。使用以下解决方法:

 myTask.ContinueWith(_ => { }, TaskContinuationOptions.ExecuteSynchronously).Wait();

你可以将这个方法变成扩展方法。

2
你能解释一下为什么这个解决方案能够解决这个问题吗? - Gusdor
3
@Gusdor你的意思是什么?它恰好做到了提问者想要的。等待一个任务而不抛出异常。 - boot4life
1
你能解释一下为什么需要使用“TaskContinuationOptions.ExecuteSynchronously”吗?对我来说,没有这个参数也能正常工作。 - user74696c
@user74696c 这是一种性能优化。它避免将新的工作项提交到线程池中。相反,当 myTask 完成时,它直接执行空委托。 - boot4life

2

根据您所写的内容,捕获异常并检查IsFaulted属性是否可能是您的解决方案?IsFaulted


我采用了Task.WaitAny方法。为了让你明白原因,被调用的代码是库代码,我无法更改,并且是从TPL外部调用的,但异常非常普遍,虽然需要处理,但如果你一直打开“在异常时中断”,那么这些异常信息就会变成很多噪音,而我总是这样做。 - Julian Birch

1
在任务内捕获异常并将其作为结果返回?
var task1 = Task.Factory.StartNew(() =>
{
    try
    {
        throw new MyCustomException("I'm bad, but not too bad!");
    }
    catch(Exception ex)
    {
        return new Result { Error = ex };
    }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接