从异步方法的同步部分处理异常 [英] Handling exceptions from the synchronous part of async method
问题描述
我处理,我开始任务可能抛出,同时仍然在初始线程上执行同步的情况。事情是这样的,为了说明:
静态异步任务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屋!