我试图更好地理解异步编程的基础知识,所以我创建了以下代码段:
private void TaskContinuations()
{
// Task for counting prime numbers
Task<int> primeNumberTask = Task.Run(() =>
Enumerable.Range(2, 3000000)
.Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1)
.All(i => n % i > 0)));
var awaiter = primeNumberTask.GetAwaiter();
// primeNumberTask.Wait(); // Option 1: Waiting but not printing (rather unexpected here)
awaiter.OnCompleted(() =>
{
var result = awaiter.GetResult();
Console.WriteLine(result);
});
//primeNumberTask.Wait(); // Option 2: Waiting but not printing (kind of expected here)
Console.Read(); // Option 3: Works and prints as expected
}
我知道我必须阻止主UI任务停止以防止后台任务被终止.所以Console.Read();阻止UI线程,如果我在素数任务完成之前没有按键,则控制台中的输出正确216816.直到此处全部清除.
所以我认为调用Wait();因为主UI任务阻塞并等待素数任务完成,所以会产生相同的效果(至少在OnCompleted回调之前调用).这确实发生了,但在两种情况下(选项1和选项2),主线程等待然后终止而没有任何输出.
这是为什么?我对这种行为的期望如下(可能有点偏差,因此我的问题)
>主UI线程等待/阻止任务直到完成,订阅已完成的任务,因此回调立即执行,输出打印为216816.但这不会发生!
要么
>主UI线程等待/阻塞,当任务完成时,它会在执行OnCompleted之前立即执行.所以这里没有预期的输出.
那么我在这里想念的是什么,能有人对我有所了解吗?
最佳答案 首先,
according MSDN,你不应该使用OnCompleted方法
This API supports the product infrastructure and is not intended to be used directly from your code.
第二件事是,在任务完成后,正在调用OnCompleted方法.所以你在这里有一个竞争条件 – 任务完成,.Net尝试在它之后执行该方法,但代码中没有任何东西在等待等待,所以程序在打印任何东西之前退出.
在这种情况下你应该使用的代码是ContinueWith
task,等待第二个任务:
private void TaskContinuations()
{
// Task for counting prime numbers
var primeNumberTask = Task.Run(() =>
Enumerable.Range(2, 3000000)
.Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1)
.All(i => n % i > 0)));
// continuation
var awaiterTask = primeNumberTask.ContinueWith(t =>
{
var result = t.Result;
Console.WriteLine(result);
});
// wait for everything to execute
awaiterTask.Wait();
}
请注意,ContinueWith
has it’s own pitfalls,应谨慎使用.关于延续技术的另一篇文章可以在here找到.