从异步方法的同步部分处理异常 [英] Handling exceptions from the synchronous part of async method

查看:175
本文介绍了从异步方法的同步部分处理异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 静态异步任务TestAsync()
{
    VAR随机=新的随机(Environment.TickCount)。接下来();
    如果(随机%2!= 0)
        抛出新ApplicationException的(第一);

    等待Task.Delay(2000);
    Console.WriteLine(等待之后Task.Delay);
    抛出新ApplicationException的(第二);
}
 

从调用code,我希望能够捕获任何异常,从同步的部分(即可能抛出,直到等待Task.Delay())。以下是我正在这样做是:

 静态无效的主要(字串[] args)
{
    尝试
    {
        变种任务= TestAsync();
        如果(task.IsFaulted)
            task.GetAwaiter()调用getResult()。
        Console.WriteLine(TestAsync继续异步......);
    }
    赶上(例外五)
    {
        Console.WriteLine(错误:+ e.ToString());
    }

    Console.WriteLine(preSS Enter键退出...);
    到Console.ReadLine();
}
 

这工作,虽然它看起来有点拗口,因为没有结果工作

我也试过 task.Wait()而不是 task.GetAwaiter()调用getResult() 。这总是给我 AggregateException 我要解开(而不是预期的 ApplicationException的直接)。

有没有其他的选择吗?

要解决的意见:我这样做,因为如果任务失败瞬间,我不想将它添加到的尚未完成的任务我维护列表。任务本身一无所知这样一个清单(和它不必)。我还是想记录异常,并让用户意识到这一点。我还可以做扔task.Exception ,但不会放弃与 ExceptionDispatchInfo ,<捕获的异常堆栈帧/ P>

[更新] 灵感来自于其他的答案和评论:如果我有完全的控制权 TestAsync ,我不希望引入新的类成员,我也可以做类似下面。验证参数时,问题可能会派上用场:

 静态任务TestAsync(INT延迟)
{
    如果(延迟℃下)
        抛出新ArgumentOutOfRangeException(延迟);

    FUNC&LT;任务&GT; asyncPart =异步()=&GT;
    {
        Console.WriteLine(等待Task.Delay);
        等待Task.Delay(延迟);
        抛出新ApplicationException的(第二);
    };

    返回asyncPart();
}
 

解决方案

我把它拆分成两部分,而不是依赖于 task.GetAwaiter()调用getResult()工作。我怕有人维护 TestAsync 可能在无意间打破东西的未来。

这是我会怎么写。这应该preserve你有问题,但我觉得它更明显发生了什么:

 静态任务测试()
{
    VAR随机=新的随机(Environment.TickCount)。接下来();
    如果(随机%2!= 0)
        抛出新ApplicationException的(第一);

    返回TestAsync();
}

静态异步任务TestAsync()
{
    等待Task.Delay(2000);
    Console.WriteLine(等待之后Task.Delay);
    抛出新ApplicationException的(第二);
}



静态无效的主要(字串[] args)
{
    尝试
    {
        测试();
        Console.WriteLine(TestAsync继续异步......);
    }
    赶上(例外五)
    {
        Console.WriteLine(错误:+ e.ToString());
    }

    Console.WriteLine(preSS Enter键退出...);
    到Console.ReadLine();
}
 

I'm dealing with the situation where the task I start may throw, while still executing synchronously on the initial thread. Something like this, for illustrative purposes:

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");
}

From the calling code, I'd like to be able to catch any exceptions, possibly thrown from the synchronous part (i.e., until await Task.Delay()). Here's how I'm currently doing it:

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();
}

This works, although it looks a bit mouthful, as there is no Result on the Task.

I've also tried task.Wait() instead of task.GetAwaiter().GetResult(). That always gives me AggregateException which I have to unwrap (rather than expected ApplicationException directly).

Is there any other options?

[EDITED] To address the comments: I do this, because if the task fails instantly, I don't want to add it to the list of the pending tasks I maintain. The task itself knows nothing about such a list (and it doesn't have to). I still want to log the exception, and make user aware of it. I could also do throw task.Exception, but that wouldn't give the exception stack frame captured with ExceptionDispatchInfo.

[UPDATE] Inspired by other answers and comments: if I have full control over TestAsync and I don't want introducing new class members, I also could do something like below. It might come handy when validating arguments:

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();
}

解决方案

I'd split it into two parts, rather than relying on task.GetAwaiter().GetResult() to work. I'd be afraid that someone maintaining TestAsync could unwittingly break things in the future.

This is how I would write it. This should preserve the behavior you've got, but I find it more obvious what's going on:

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();
}

这篇关于从异步方法的同步部分处理异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆