立即从异步方法投掷

时间:2014-03-29 00:14:13

标签: c# .net task-parallel-library async-await

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

这种情况还有其他选择吗?这可能是一个人为的问题吗?

2 个答案:

答案 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函数立即抛出,这是有道理的,因为没有任何东西取决于它的结果,并且没有任务要传递。

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