【发布时间】:2015-10-03 09:40:05
【问题描述】:
考虑以下实现,一个方法接受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
【问题讨论】:
-
这是一个标准的线程竞争错误,IProgress.Report() 使用 SynchronizationContext.Post() 方法来调用委托。不像 Send() 那样同步,它稍后完成。仅将 IProgress 用于向用户界面报告对时间要求不严格的进度。
标签: c# multithreading task iprogress