没有从异步委托提供的故障 CountinueWith 中捕获异常
No exceptions caught from faulted CountinueWith supplied with async delegate
以下程序我想捕获我认为在访问时应该发生的异常 t.Result
class Program
{
static void Main(string[] args)
{
Task.Run(async () => await Test()).Wait();
Console.ReadKey();
}
private static async Task Test()
{
var t1 = Task.Run<int>(() => { throw new Exception("1"); return 1; });
var tasks = new[]
{
//t1.ContinueWith(t => {
// var y = t.Result + 1;
// }),
//t1.ContinueWith(t => {
// var y = t.Result + 1;
// })
t1.ContinueWith(async t =>
{
var y = t.Result + 1;
await Task.Delay(100);
}),
t1.ContinueWith(async t =>
{
var y = t.Result + 1;
await Task.Delay(100);
}),
};
try
{
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught");
}
}
}
但是如果我使用异步委托,则不会捕获异常。两个任务都出错,但它们的异常为空。
问题
- 有什么窍门,我在哪里可以阅读更多详细信息?
- 如果我以某种方式失去了等待,我可以确定在 Task.WhenAll returns 之后继续完成吗?
造成混淆的原因是 Task.Run
与 Task.Factory.StartNew
和 Task.ContinueWith
的预期用法不同。
Task.Run
,通过其重载,可以接受同步委托(Action
或 Func<TResult>
)或异步委托(Func<Task>
或 Func<Task<TResult>>
).如果指定了异步委托,Task.Run
负责解包表示异步操作的内部任务,并返回 that 作为结果。
Task.ContinueWith
, on the other hand, does not recognize asynchronous delegates. If you pass a Func<Task, Task>
delegate to ContinueWith
, it will simply wrap the asynchronous operation within its own outer task, returning Task<Task>
as the result. (Same applies to TaskFactory.StartNew
.) 因此,您的 Task.WhenAll
只会等待外部任务,而不是异步操作。这不是你想要的。
要展开内部异步操作,您只需调用 Unwrap
:
t1.ContinueWith(async t =>
{
var y = t.Result + 1;
await Task.Delay(100);
}).Unwrap(),
有关此问题的讨论,请参阅 Task.Run vs Task.Factory.StartNew. (The handling of asynchronous delegates in ContinueWith
is similar to StartNew
.) For the rationale behind unwrapping, see How to: Unwrap a Nested Task。
此外,我建议您避免混合使用 await
和 ContinueWith
。在您的情况下,您可以在异步委托中等待 t1
的结果,而不是向其注册延续。您将需要一个 self-invoking 匿名函数以单一方法执行此操作……
((Func<Task>)(async () =>
{
var y = await t1 + 1;
await Task.Delay(100);
}))(),
…但是如果你把它拆分成一个单独的方法,阅读起来会更清楚:
private static async Task Test()
{
var t1 = Task.Run<int>(() => { throw new Exception("1"); return 1; });
var tasks = new[]
{
IncrementAndDelayAsync(t1),
IncrementAndDelayAsync(t1),
};
// ...
}
private static async Task IncrementAndDelayAsync(Task<int> t1)
{
var y = await t1 + 1;
await Task.Delay(100);
}
以下程序我想捕获我认为在访问时应该发生的异常 t.Result
class Program
{
static void Main(string[] args)
{
Task.Run(async () => await Test()).Wait();
Console.ReadKey();
}
private static async Task Test()
{
var t1 = Task.Run<int>(() => { throw new Exception("1"); return 1; });
var tasks = new[]
{
//t1.ContinueWith(t => {
// var y = t.Result + 1;
// }),
//t1.ContinueWith(t => {
// var y = t.Result + 1;
// })
t1.ContinueWith(async t =>
{
var y = t.Result + 1;
await Task.Delay(100);
}),
t1.ContinueWith(async t =>
{
var y = t.Result + 1;
await Task.Delay(100);
}),
};
try
{
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught");
}
}
}
但是如果我使用异步委托,则不会捕获异常。两个任务都出错,但它们的异常为空。
问题
- 有什么窍门,我在哪里可以阅读更多详细信息?
- 如果我以某种方式失去了等待,我可以确定在 Task.WhenAll returns 之后继续完成吗?
造成混淆的原因是 Task.Run
与 Task.Factory.StartNew
和 Task.ContinueWith
的预期用法不同。
Task.Run
,通过其重载,可以接受同步委托(Action
或 Func<TResult>
)或异步委托(Func<Task>
或 Func<Task<TResult>>
).如果指定了异步委托,Task.Run
负责解包表示异步操作的内部任务,并返回 that 作为结果。
Task.ContinueWith
, on the other hand, does not recognize asynchronous delegates. If you pass a Func<Task, Task>
delegate to ContinueWith
, it will simply wrap the asynchronous operation within its own outer task, returning Task<Task>
as the result. (Same applies to TaskFactory.StartNew
.) 因此,您的 Task.WhenAll
只会等待外部任务,而不是异步操作。这不是你想要的。
要展开内部异步操作,您只需调用 Unwrap
:
t1.ContinueWith(async t =>
{
var y = t.Result + 1;
await Task.Delay(100);
}).Unwrap(),
有关此问题的讨论,请参阅 Task.Run vs Task.Factory.StartNew. (The handling of asynchronous delegates in ContinueWith
is similar to StartNew
.) For the rationale behind unwrapping, see How to: Unwrap a Nested Task。
此外,我建议您避免混合使用 await
和 ContinueWith
。在您的情况下,您可以在异步委托中等待 t1
的结果,而不是向其注册延续。您将需要一个 self-invoking 匿名函数以单一方法执行此操作……
((Func<Task>)(async () =>
{
var y = await t1 + 1;
await Task.Delay(100);
}))(),
…但是如果你把它拆分成一个单独的方法,阅读起来会更清楚:
private static async Task Test()
{
var t1 = Task.Run<int>(() => { throw new Exception("1"); return 1; });
var tasks = new[]
{
IncrementAndDelayAsync(t1),
IncrementAndDelayAsync(t1),
};
// ...
}
private static async Task IncrementAndDelayAsync(Task<int> t1)
{
var y = await t1 + 1;
await Task.Delay(100);
}