考虑以下实现,方法接受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
答案 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>
包装。