searchAll()
-async
概念很容易理解,但是,我很难掌握await
。
在下面的示例中,我要一个接一个地运行2个异步任务(LoadAsync和ComputeAsync),与此同时,我想执行DoSomethingElse。方法1是我熟悉的方法。
方法2,方法3和方法4是否正确获得与方法1相同的结果?有人可以解释/评论一下幕后的区别吗?谢谢!
方法1-在ContinueWith
函数内部使用await
async
方法2-在public async int LoadAndComputeAsync
{
var data = await LoadAsync();
return await ComputeAsync(data);
}
Task<int> task1 = LoadAndComputeAsync();
DoSomethingElse();
int result = await task1;
中同步执行
Task.Run
方法3-将Task<int> task2 = Task.Run(() => {
var data = LoadAsync().Result;
return ComputeAsync(data).Result;
});
DoSomethingElse();
int result = await task2;
与ContinueWith
和async
一起使用
Unwrap
方法4-在Task<int> task3 = LoadAsync().ContinueWith(async(t) => {
var data = t.Result;
return await ComputeAsync(data);
}).Unwrap();
DoSomethingElse();
int result = await task3;
中同步执行
ContinueWith
答案 0 :(得分:2)
但是,我很难掌握
ContinueWith
。
使用ContinueWith
的最简单方法是拼写为“ await”。
不,认真。 ContinueWith
is a low-level API with surprising default behavior。这将使您的代码更复杂,更难以维护,同时没有任何好处。所以我的问题是“为什么?”
也就是说,以下内容将为您提供一些答案,但这些答案仅用于教学目的,而不用于 生产代码。
首先,Task<T>.Result
具有不同的异常处理行为;它将所有异常包装在AggregateException
中,而不是直接引发它们。这是因为Task<T>
最初是为并行编程而不是异步编程而设计的。但是当添加async
/ await
时,Microsoft决定只重用现有的Task
/ Task<T>
类型,而不是创建更多异步本地类型。对于异步代码,将.Result
替换为.GetAwaiter().GetResult()
。
接下来,async
不会将工作排队到线程池中。 Task.Run
可以。这是方法2的另一个区别。
您的方法3非常接近。如果将.Result
替换为.GetAwaiter().GetResult()
,您仍然会遇到TaskScheduler
使用的ContinueWith
的问题,该问题默认为TaskScheduler.Current
,可能不是您想要什么(在异步代码中,通常不是)。在未指定ContinueWith
的情况下,切勿使用TaskScheduler
。另外,将ContinueWith
与async
一起使用也很奇怪-如果您仍在使用async
,为什么不直接使用方法#1?
方法4确实阻止了ContinueWith
中的线程。
如果您想真实地复制方法1,则需要:
AggregateException
中。TaskScheduler
传递给ContinueWith
。ContinueWith
使用其他适当的标志,以使其行为更加异步友好。这是一个例子:
// Original
public async Task<int> LoadAndComputeAsync()
{
var data = await LoadAsync();
return await ComputeAsync(data);
}
// Using ContinueWith
public Task<int> LoadAndComputeTheHardWayAsync()
{
var scheduler = SynchronizationContext.Current != null ?
TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current;
return LoadAsync().ContinueWith(t =>
{
var data = t.GetAwaiter().GetResult();
return ComputeAsync(data);
},
CancellationToken.None,
TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.ExecuteSynchronously,
scheduler).Unwrap();
}
答案 1 :(得分:0)