并不是iprogress的每一个结果都是如此.来自于任务
本文关键字:来自于 任务 iprogress 每一个 结果 并不是 | 更新日期: 2023-09-27 18:15:59
考虑以下实现,一个方法接受一个IProgress<int>
,迭代10000个对象。numbers
数组变量返回10000个对象,但IProgress<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.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
这可能是因为Progress<T>
的实现方式。当创建时,Progress<T>
捕获同步上下文并使用它来执行i => result.Add(i)
。由于您正在运行一个测试,所以我假设没有同步上下文。在本例中,Progress<T>
使用默认的SynchronizationContext
,它将工作项发布到线程池(ThreadPool.QueueUserWorkItem
)。在线程池处理所有队列项之前,任务就完成了,这很好地解释了结果不一致的原因。
检查这种情况的简单方法:将IProgress<int>
参数更改为Action<int>
,并直接传递i => result.Add(i)
委托,而不使用Progress<T>
包装。