从async Task
方法抛出的异常的normal behavior是保持休眠状态,直到稍后观察,或者直到任务被垃圾收集。
我可以想到我可能想立即投掷的情况。这是一个例子:
public static async Task TestExAsync(string filename)
{
// the file is missing, but it may be there again
// when the exception gets observed 5 seconds later,
// hard to debug
if (!System.IO.File.Exists(filename))
throw new System.IO.FileNotFoundException(filename);
await Task.Delay(1000);
}
public static void Main()
{
var task = TestExAsync("filename");
try
{
Thread.Sleep(5000); // do other work
task.Wait(); // wait and observe
}
catch (AggregateException ex)
{
Console.WriteLine(new { ex.InnerException.Message, task.IsCanceled });
}
Console.ReadLine();
}
我可以使用async void
绕过这个,它会立即抛出:
// disable the "use await" warning
#pragma warning disable 1998
public static async void ThrowNow(Exception ex)
{
throw ex;
}
#pragma warning restore 1998
public static async Task TestExAsync(string filename)
{
if (!System.IO.File.Exists(filename))
ThrowNow(new System.IO.FileNotFoundException(filename));
await Task.Delay(1000);
}
现在我可以使用Dispatcher.UnhandledException
或AppDomain.CurrentDomain.UnhandledException
在现场处理此异常,至少立即引起用户注意。
这种情况还有其他选择吗?这可能是一个人为的问题吗?
答案 0 :(得分:5)
如果你真的想这样做,你可以使用相同的方法Jon Skeet used in his reimplementation of LINQ:创建一个可以抛出或调用真正的异步方法的同步方法:
public static Task TestExAsync(string filename)
{
if (!System.IO.File.Exists(filename))
throw new System.IO.FileNotFoundException(filename);
return TestExAsyncImpl(filename);
}
private static async Task TestExAsyncImpl(string filename)
{
await Task.Delay(1000);
}
请记住,我认为假设Task
返回方法不直接投掷是正常的。例如,您可以使用Task.WhenAll()
在正常情况下从多个操作中获取所有异常,但是这种方法在立即抛出异常时不会起作用。
答案 1 :(得分:1)
我认为正常行为是恰当的。你的线程依赖于 async
函数的结果来进行下一次处理,所以应该在你的线程上抛出异常。
然后,您的线程代码可以采取适当的措施从异常中恢复。由于您可以通过任务并启动许多任务,因此您的恢复代码可能位于您需要获取任务结果的其他位置,而不是原始调用代码。如果立即抛出异常,则可能会将抛出恢复代码。
asynch
void函数立即抛出,这是有道理的,因为没有任何东西取决于它的结果,并且没有任务要传递。