并非每个IProgress <int>的结果都来自任务

时间:2015-10-03 00:16:24

标签: c# multithreading task iprogress

考虑以下实现,方法接受IProgress<int>,迭代超过10000个对象。 numbers数组变量返回10000个对象,但IProgress<int>仅报告9970 - 9980个对象。它每次运行都有所不同,因此有些人会失去&#34;。

    protected async override Task<int[]> CollectDataAsyncImpl(IProgress<int> progress) {                
        return await Task.Run<int[]>(() => {

            var numbers = new List<int>();

            foreach (var idx in new Int32Range(1, 10000).AsEnumerable().Index()) {                                            

                numbers.Add(idx.Value);                    

                if (progress != null) {
                    progress.Report(idx.Value);
                }

            }

            return numbers.ToArray();
        });
    }

作为参考,这是我跑的测试。它在第三个断言Assert.Equal(10000, result[9999]);处失败。

[Fact]
async void ReportsProgress() {            
    var sut = new IntegerCollector();
    var result = new List<int>();
    var output = await sut.CollectDataAsync(new Progress<int>(i => result.Add(i)));
    Assert.Equal(10000, output.Length);
    Assert.Equal(1, result[0]);
    Assert.Equal(10000, result[9999]);
}

显然,我做错了什么,或者我不了解任务/线程的内部。我对IProgress<int>new Progress<int>(i => result.Add(i))的实施不正确吗?我应该使该线程安全吗?如果是,我该怎么做?

GitHub有你可以克隆的代码。如果需要,请进行测试:https://github.com/KodeFoxx/Kf.DataCollection/tree/master/Source/Kf.DataCollection

1 个答案:

答案 0 :(得分:2)

可能是因为Progress<T>的实施方式。创建时,Progress<T>捕获同步上下文并使用它来执行i => result.Add(i)。由于您正在运行测试,我假设没有同步上下文。在这种情况下,Progress<T>使用默认的SynchronizationContext,它将工作项发布到线程池(ThreadPool.QueueUserWorkItem)。您的任务在线程池处理所有排队项之前完成,并且它完美地解释了结果不一致。

检查是否是这种情况的简单方法:将IProgress<int>参数更改为Action<int>并直接传递i => result.Add(i)委托,而不用Progress<T>包装。