Ian*_*ink 205 .net c# task-parallel-library async-await .net-4.5
我有3个任务:
private async Task<Cat> FeedCat() {}
private async Task<House> SellHouse() {}
private async Task<Tesla> BuyCar() {}
Run Code Online (Sandbox Code Playgroud)
他们都需要在我的代码可以继续之前运行,我也需要每个代码的结果.结果没有任何共同之处
如何调用并等待3个任务完成然后获得结果?
Ste*_*ary 349
使用后WhenAll,您可以单独提取结果await:
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
await Task.WhenAll(catTask, houseTask, carTask);
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
Run Code Online (Sandbox Code Playgroud)
你也可以使用Task.Result(因为你知道他们已经成功完成了).但是,我建议使用,await因为它显然是正确的,但Result在其他情况下可能会导致问题.
Ser*_*rvy 81
await在启动它们之后,只分别执行三个任务.
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
Run Code Online (Sandbox Code Playgroud)
Joe*_*ler 32
如果你正在使用C#7,你可以使用像这样的方便的包装方法......
public static class TaskEx
{
public static async Task<(T1, T2)> WhenAll<T1, T2>(Task<T1> task1, Task<T2> task2)
{
return (await task1, await task2);
}
}
Run Code Online (Sandbox Code Playgroud)
...当您想要等待具有不同返回类型的多个任务时,启用这样的方便语法.当然,您必须为不同数量的任务进行多次重载.
var (someInt, someString) = await TaskEx.WhenAll(GetIntAsync(), GetStringAsync());
Run Code Online (Sandbox Code Playgroud)
Ree*_*sey 11
您可以将它们存储在任务中,然后等待它们:
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
await Task.WhenAll(catTask, houseTask, carTask);
Cat cat = await catTask;
House house = await houseTask;
Car car = await carTask;
Run Code Online (Sandbox Code Playgroud)
Mar*_*ell 11
给出了三个任务 - FeedCat(),SellHouse()并且BuyCar(),有两个有趣的案例:要么它们都同步完成(出于某种原因,可能是缓存或错误),要么它们没有.
让我们说,从问题:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
// what here?
}
Run Code Online (Sandbox Code Playgroud)
现在,一个简单的方法是:
Task.WhenAll(x, y, z);
Run Code Online (Sandbox Code Playgroud)
但......处理结果不方便; 我们通常想要await:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
await Task.WhenAll(x, y, z);
// presumably we want to do something with the results...
return DoWhatever(x.Result, y.Result, z.Result);
}
Run Code Online (Sandbox Code Playgroud)
但这会产生大量开销并分配各种数组(包括params Task[]数组)和列表(内部).它有效,但它不是伟大的IMO.在许多方面,使用操作更简单async,只需await依次操作:
async Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
// do something with the results...
return DoWhatever(await x, await y, await z);
}
Run Code Online (Sandbox Code Playgroud)
相反,一些上述的意见,使用await代替Task.WhenAll使没有区别的任务是如何运行(同时,顺序等).在最高级别,Task.WhenAll 早于对async/的良好编译器支持await,并且在这些事物不存在时很有用.当您拥有任意数组的任务时,它也很有用,而不是3个谨慎的任务.
但是:我们仍然存在async/ await为继续产生/ 产生大量编译器噪声的问题.如果任务可能实际上同步完成,那么我们可以通过在具有异步回退的同步路径中构建来优化它:
Task<string> DoTheThings() {
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
async Task Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await x, await y, await z);
}
Run Code Online (Sandbox Code Playgroud)
这种"具有异步回退的同步路径"方法越来越普遍,特别是在同步完成相对频繁的高性能代码中.请注意,如果完成始终是真正的异步,它将完全没有帮助.
适用于此处的其他内容:
对于最近的C#,async回退方法的一个常见模式通常是作为本地函数实现的:
Task<string> DoTheThings() {
async Task<string> Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
Task<Cat> x = FeedCat();
Task<House> y = SellHouse();
Task<Tesla> z = BuyCar();
if(x.Status == TaskStatus.RanToCompletion &&
y.Status == TaskStatus.RanToCompletion &&
z.Status == TaskStatus.RanToCompletion)
return Task.FromResult(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
Run Code Online (Sandbox Code Playgroud)喜欢ValueTask<T>到Task<T>是否有东西的好机会能完全同步与许多不同的返回值:
ValueTask<string> DoTheThings() {
async ValueTask<string> Awaited(ValueTask<Cat> a, Task<House> b, Task<Tesla> c) {
return DoWhatever(await a, await b, await c);
}
ValueTask<Cat> x = FeedCat();
ValueTask<House> y = SellHouse();
ValueTask<Tesla> z = BuyCar();
if(x.IsCompletedSuccessfully &&
y.IsCompletedSuccessfully &&
z.IsCompletedSuccessfully)
return new ValueTask<string>(
DoWhatever(a.Result, b.Result, c.Result));
// we can safely access .Result, as they are known
// to be ran-to-completion
return Awaited(x, y, z);
}
Run Code Online (Sandbox Code Playgroud)如果可能的话,宁愿IsCompletedSuccessfully到Status == TaskStatus.RanToCompletion; 这现在存在于.NET Core中Task,并且随处可见ValueTask<T>
如果您尝试记录所有错误,请确保在代码中保留Task.WhenAll行,大量注释表明您可以删除它并等待单个任务。Task.WhenAll对于错误处理非常重要。如果没有此行,则可能会为未观察到的异常保留代码。
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
await Task.WhenAll(catTask, houseTask, carTask);
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
Run Code Online (Sandbox Code Playgroud)
想象一下,FeedCat在以下代码中引发异常:
var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();
var cat = await catTask;
var house = await houseTask;
var car = await carTask;
Run Code Online (Sandbox Code Playgroud)
在那种情况下,您将永远不会等待houseTask或carTask。这里有3种可能的情况:
FeedCat失败时,SellHouse已成功完成。在这种情况下,你很好。
SellHouse尚不完整,并且在某些时候会因异常而失败。未观察到异常,它将在终结器线程上重新引发。
SellHouse不完整,其中包含等待。如果您的代码在ASP.NET SellHouse中运行,一旦其中的一些等待完成,它就会失败。发生这种情况的原因是,一旦FeedCat发生故障,您基本上会立即开火并忘记通话,并且同步上下文丢失。
这是情况(3)的错误:
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
at System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter()
at System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action)
at System.Threading.Tasks.Task.Execute()
--- End of inner exception stack trace ---
---> (Inner Exception #0) System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
at System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter()
at System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action)
at System.Threading.Tasks.Task.Execute()<---
Run Code Online (Sandbox Code Playgroud)
对于情况(2),您将获得类似的错误,但具有原始异常堆栈跟踪。
对于.NET 4.0及更高版本,您可以使用TaskScheduler.UnobservedTaskException捕获未观察到的异常。对于.NET 4.5及更高版本,.NET 4.0默认会吞没不可观察的异常,不可观察的异常将使您的进程崩溃。
此处有更多详细信息:.NET 4.5中的任务异常处理
| 归档时间: |
|
| 查看次数: |
76552 次 |
| 最近记录: |