这是一个"学术"问题,而不是影响我目前正在以官方身份工作的任何事情,但我想这里有人可以向我解释这一点。
我在LinqPAD中玩CancellationToken
(我提到这个细节,所以不熟悉它的人知道Dump()
基本上是Console.WriteLine()
的简写)我正在尝试一个场景,我的主线程在我的任务完成时睡眠。这是在运行几个取消任务的变体之后完成的,然后才能完成与IsCanceled
属性一起播放并验证取消时无法访问Result
属性。这是我的虚拟LinqPAD程序的样子:
void Main()
{
var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => Test())
.ContinueWith(t => t.Result, cts.Token)
.ContinueWith(t =>
{
if (t.IsCanceled)
{
"Cancelled".Dump();
}
else if (t.IsCompleted)
{
"Completed".Dump();
t.Result.Sum (r => r).Dump();
}
});
//sleep long enough to make sure the Task completes
Thread.Sleep(6000);
}
private IEnumerable<int> Test()
{
foreach(var i in Enumerable.Range(0, 10))
{
yield return i;
Task.Delay(500).Wait();
}
}
这种行为 - 主要是 - 我的期望:输出是&#34;已完成&#34;其次是&#34; 45&#34;。我没想到的是&#34; 45&#34;在Thread.Sleep()调用完成之前没有输出,但是&#34;已完成&#34;立即输出。
所以我的问题是:如何立即评估我的任务的IsCompleted
标志,但是直到调用线程从Result
调用返回之后才会评估Thread.Sleep()
属性?另外,有没有什么方法可以重构这个,既可以立即输出,也可以等待Sleep()
调用的结束?
我知道这个例子是人为的,但我无法绕过这种做作的行为
的修改
其他问题:这种观察到的行为并不意味着我可以处于任务Result
属性在评估IsCompleted
和我能够评估{{}}之间变化的状态{1}}?
编辑2
经过进一步的调查,我能够回答我自己的一个问题:我可以强制完成&#34;已完成&#34;通过更改Result
的行为以返回IList&lt;&gt;来输出等到Thread.Sleep()
完成后这是在方法中构建的,而不是Test()
IEnumerable。我理解IEnumerable一般是如何工作的,但我通过介绍任务看到的行为是让我失望的原因。
答案 0 :(得分:5)
这是因为'收益率回报'和IEnumerable&lt;&gt;的工作原理。
因此,当您调用Test()时,它实际上并不运行循环,它只返回一个IEnumerable&lt;&gt;。
要证明您可以运行此代码 -
var stopwatch = Stopwatch.StartNew();
var results = Test();
Console.WriteLine("Time after running Test {0}", stopwatch.ElapsedMilliseconds);
foreach (var result in results)
{
Console.WriteLine("Looping {0}", stopwatch.ElapsedMilliseconds);
}
所以只有在这行代码之后 - t.Result.Sum(r =&gt; r).Dump(); 你开始循环并等待Task.Delay(500).Wait(); 完成后将总结数字并将其写入控制台。