处理异步方法的同步部分中的异常

时间:2014-01-10 22:50:06

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

我正在处理我开始的任务可能抛出的情况,同时仍在初始线程上同步执行。出于说明的目的,这样的事情:

static async Task TestAsync()
{
    var random = new Random(Environment.TickCount).Next();
    if (random % 2 != 0)
        throw new ApplicationException("1st");

    await Task.Delay(2000);
    Console.WriteLine("after await Task.Delay");
    throw new ApplicationException("2nd");
}

从调用代码中,我希望能够捕获可能从同步部分抛出的任何异常(即,直到await Task.Delay())。以下是我目前正在做的事情:

static void Main(string[] args)
{
    try
    {
        var task = TestAsync();
        if (task.IsFaulted)
            task.GetAwaiter().GetResult();
        Console.WriteLine("TestAsync continues asynchronously...");
    }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e.ToString());
    }

    Console.WriteLine("Press Enter to exit...");
    Console.ReadLine();
}

虽然看起来有点满口,但Result上没有Task

我也尝试过task.Wait()而不是task.GetAwaiter().GetResult()。这总是给我AggregateException我必须解开(而不是直接预期ApplicationException。)

还有其他选择吗?

[已编辑] 要解决这些问题:我这样做,因为如果任务立即失败,我不想将其添加到我维护的待处理任务列表中。任务本身对这样的列表一无所知(并且它没有必要)。我仍然想记录异常,并让用户知道它。我也可以执行throw task.Exception,但这不会使用ExceptionDispatchInfo捕获异常堆栈帧。

[更新] 受到其他答案和评论的启发:如果我完全控制TestAsync并且我不想引入新的班级成员,我也可以执行以下操作。验证参数时可能会派上用场:

static Task TestAsync(int delay)
{
    if (delay < 0)
        throw new ArgumentOutOfRangeException("delay");

    Func<Task> asyncPart = async () =>
    {
        Console.WriteLine("await Task.Delay");
        await Task.Delay(delay);
        throw new ApplicationException("2nd");
    };

    return asyncPart();
}

2 个答案:

答案 0 :(得分:1)

我将它分成两部分,而不是依靠task.GetAwaiter().GetResult()来工作。我担心维持TestAsync的人可能会在将来无意中破坏事情。

这就是我写它的方式。这应该保留你已经拥有的行为,但我发现它发生的更明显:

static Task Test()
{
    var random = new Random(Environment.TickCount).Next();
    if (random % 2 != 0)
        throw new ApplicationException("1st");

    return TestAsync();
}

static async Task TestAsync()
{
    await Task.Delay(2000);
    Console.WriteLine("after await Task.Delay");
    throw new ApplicationException("2nd");
}



static void Main(string[] args)
{
    try
    {
        Test();
        Console.WriteLine("TestAsync continues asynchronously...");
    }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e.ToString());
    }

    Console.WriteLine("Press Enter to exit...");
    Console.ReadLine();
}

答案 1 :(得分:-1)

一般来说,异常不应该用于应用程序中的常规错误处理。抛出“特殊”情况的例外情况,因为出现意外情况并且您需要进行一次硬停止,程序无法继续。

当然,我并不确切知道你的用例是什么,但每当我使用异步任务时,意外失败的部分通常也应该是异步的部分(例如连接到数据库)。

无论如何,我将如何将TestAsync方法放入自己的类中。然后,您可以使用方法(或属性)bool TestAsync.IsValid来确定任务是否已准备就绪并应排队等待执行;然后,如果答案为真,您可以运行异步任务:TestAsync.RunAsync()