任务结果评估的时间安排

时间:2013-12-24 17:48:37

标签: .net multithreading task-parallel-library c#-5.0

这是一个"学术"问题,而不是影响我目前正在以官方身份工作的任何事情,但我想这里有人可以向我解释这一点。

我在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一般是如何工作的,但我通过介绍任务看到的行为是让我失望的原因。

1 个答案:

答案 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(); 完成后将总结数字并将其写入控制台。