3

考虑以下实现,一个方法接受一个IProgress<int>,迭代超过 10000 个对象。数组变量返回 10000 个对象,numbersIProgress<int>报告仅在 9970 - 9980 个对象之间。每次运行都会有所不同,因此有些人会“迷路”。

    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 失败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

4

1 回答 1

3

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

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

于 2015-10-03T01:40:05.940 回答