嵌套 async/await 未抛出异常
Exception not thrown from nested async/await
我有一个 .NET Core 控制台应用程序。永远不会抛出嵌套 async/await 中发生的异常:
static async Task Main(string[] args)
{
try
{
var f = new TaskFactory(TaskScheduler.Current);
await f.StartNew(async () =>
{
var x = 0;
if (x == 0)
throw new Exception("we have a problem");
await Task.Delay(1);
});
}
catch(Exception)
{
// never reaches here
}
}
如果我删除内部 async
,并放弃对 await Task.Delay(1)
的调用,则会捕获异常。
这是一个经典的陷阱。 TaskFactory
期望 Func<T>
和 returns 一个 Task<T>
。在你的例子中,T
是 Task
,因此你最终得到一个 Task<Task>
并且你需要等待内部和外部任务。为此使用 Unwrap
:
await f.StartNew(async () =>
{
var x = 0;
if (x == 0)
throw new Exception("we have a problem");
await Task.Delay(1);
}).Unwrap();
您的错误来自StartNew。
当您等待 StartNew 时,您希望里面的作业完成。然而,你给它的工作是创建一个任务。您的 StartNew 具有以下 return 类型 Task<Task>
。
因为它是您的代码在抛出异常之前结束。
您可以通过两种不同的方式解决:
1) 等待第一次等待完成后获得的新开始结果
static async Task Main(string[] args)
{
try
{
var f = new TaskFactory(TaskScheduler.Current);
await await f.StartNew(async () =>
{
var x = 0;
if (x == 0)
throw new Exception("we have a problem");
await Task.Delay(1);
});
}
catch(Exception)
{
Console.WriteLine("Exception received");
// never reaches here
}
Console.WriteLine("Done");
}
2) 您停止使用 StartNew 并使用基本任务模式(我推荐)
static async Task Main(string[] args)
{
try
{
await Task.Run(async () =>
{
var x = 0;
if (x == 0)
throw new Exception("we have a problem");
await Task.Delay(1);
});
}
catch(Exception)
{
Console.WriteLine("Exception received");
// never reaches here
}
Console.WriteLine("Done");
}
之所以删除 Task.Delay(1)
解决了您的问题,是因为您将 StartNew 的签名更改为 Task 而不是 Task<Task>
,这使您的单次等待变得高效
我有一个 .NET Core 控制台应用程序。永远不会抛出嵌套 async/await 中发生的异常:
static async Task Main(string[] args)
{
try
{
var f = new TaskFactory(TaskScheduler.Current);
await f.StartNew(async () =>
{
var x = 0;
if (x == 0)
throw new Exception("we have a problem");
await Task.Delay(1);
});
}
catch(Exception)
{
// never reaches here
}
}
如果我删除内部 async
,并放弃对 await Task.Delay(1)
的调用,则会捕获异常。
这是一个经典的陷阱。 TaskFactory
期望 Func<T>
和 returns 一个 Task<T>
。在你的例子中,T
是 Task
,因此你最终得到一个 Task<Task>
并且你需要等待内部和外部任务。为此使用 Unwrap
:
await f.StartNew(async () =>
{
var x = 0;
if (x == 0)
throw new Exception("we have a problem");
await Task.Delay(1);
}).Unwrap();
您的错误来自StartNew。
当您等待 StartNew 时,您希望里面的作业完成。然而,你给它的工作是创建一个任务。您的 StartNew 具有以下 return 类型 Task<Task>
。
因为它是您的代码在抛出异常之前结束。
您可以通过两种不同的方式解决:
1) 等待第一次等待完成后获得的新开始结果
static async Task Main(string[] args)
{
try
{
var f = new TaskFactory(TaskScheduler.Current);
await await f.StartNew(async () =>
{
var x = 0;
if (x == 0)
throw new Exception("we have a problem");
await Task.Delay(1);
});
}
catch(Exception)
{
Console.WriteLine("Exception received");
// never reaches here
}
Console.WriteLine("Done");
}
2) 您停止使用 StartNew 并使用基本任务模式(我推荐)
static async Task Main(string[] args)
{
try
{
await Task.Run(async () =>
{
var x = 0;
if (x == 0)
throw new Exception("we have a problem");
await Task.Delay(1);
});
}
catch(Exception)
{
Console.WriteLine("Exception received");
// never reaches here
}
Console.WriteLine("Done");
}
之所以删除 Task.Delay(1)
解决了您的问题,是因为您将 StartNew 的签名更改为 Task 而不是 Task<Task>
,这使您的单次等待变得高效