立即从异步方法抛出

本文关键字:异步方法 | 更新日期: 2023-09-27 17:54:53

async Task方法抛出的异常的正常行为是保持休眠状态,直到它们稍后被观察到,或者直到任务被垃圾收集。

我可以想到我可能想立即抛出的情况。下面是一个例子:

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.UnhandledExceptionAppDomain.CurrentDomain.UnhandledException在现场处理这个异常,至少可以立即引起用户的注意。

这个场景还有其他的选择吗?这可能是人为的问题吗?

立即从异步方法抛出

如果您真的想这样做,您可以使用Jon Skeet在重新实现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()从几个操作中获取所有异常,但是当异常被立即抛出时,这种方法将不起作用。

我认为正常的行为是适当的。您的线程依赖于async函数的结果来进行下一个处理,因此应该在您的线程上抛出异常。然后,您的线程代码可以采取适当的措施从异常中恢复。因为您可以传递任务并启动许多任务,所以您的恢复代码可能位于中其他需要获得任务结果而不是原始调用代码的地方。如果立即抛出异常,则可以在恢复代码之外抛出

asynch void函数立即抛出,这是有意义的,因为没有任何东西依赖于它的结果,也没有任务要传递。

顺便说一句,异常处理的重点是从异常中恢复应用程序状态,不应该捕获任何无法恢复的异常。当抛出异常时,您的应用程序状态可能已损坏,尝试继续处理已损坏的应用程序会导致更多问题和安全漏洞。