【问题标题】:Timing of Task Result Evaluation任务结果评估时间
【发布时间】:2013-12-24 17:48:36
【问题描述】:

这是一个“学术”问题,而不是影响我目前以官方身份从事的任何工作的问题,但我认为这里有人可以向我解释这一点。

我在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();
    }
}

这表现——主要是——我所期望的:输出是“已完成”,然后是“45”。令我没想到的是,直到 Thread.Sleep() 调用完成后才输出“45”,而是立即输出了“Completed”。

所以我的问题是:如何立即评估我的任务的IsCompleted 标志,但直到调用线程从Thread.Sleep() 调用返回之后才评估Result 属性?此外,我有什么办法可以重组它,要么立即输出,要么都等待Sleep() 调用结束?

我知道这个例子是人为的,但我无法理解这种人为的行为。
编辑
附加问题:这种观察到的行为是否意味着我可能会进入任务的Result 属性在评估IsCompleted 和我能够评估Result 之间发生变化的状态?
编辑2
经过进一步调查,我能够回答我自己的一个问题:我可以通过更改 Test() 的行为来强制“完成”输出等到 Thread.Sleep() 完成后返回一个内置的 IList -在方法中而不是yield returning IEnumerable。我了解 IEnumerable 的一般工作原理,但我在这里看到的通过引入任务的行为让我很反感。

【问题讨论】:

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


    【解决方案1】:

    这是因为 'yield return' 和 IEnumerable 的工作方式。

    因此,当您调用 Test() 时,它实际上并没有运行循环,它只是返回一个 IEnumerable。

    为了证明你可以运行这段代码 -

        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 => r).Dump(); 您开始循环并等待 Task.Delay(500).Wait(); 完成后将汇总数字并将其写入控制台。

    【讨论】:

    • 我明白这一点,但我在不同的任务中访问 IEnumerable(通过 ContinueWith 构造),那么为什么我的主线程中的 Sleep 会影响它呢?跨度>
    • 我不明白你的问题。你在主线程中谈论什么效果?有2个线程。 1 - 主要,2 - 创建的任务。
    • 我说的效果是为什么Sum() 直到Thread.Sleep() 完成才执行。
    • 啊,我完全忘记了我在Test()方法中留下的Task.Delay()!这完美地解释了它。对不起,由于我缺乏阅读理解能力。
    猜你喜欢
    • 2011-07-17
    • 2020-09-18
    • 1970-01-01
    • 1970-01-01
    • 2013-01-27
    • 2019-08-14
    • 2020-07-18
    • 2020-01-19
    • 1970-01-01
    相关资源
    最近更新 更多