不是每一个IProgress<int>的结果都是从任务中出来的
Not every result of IProgress<int> comes out of the task
考虑以下实现,一个方法接受一个 IProgress<int>
,迭代 10000 个对象。 numbers
数组变量 returns 10000 个对象,但 IProgress<int>
仅报告 9970 - 9980 个对象。它因 运行 而异,所以有些人得到 "lost"。
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]);
}
显然我做错了什么,或者我不了解task/threading的内部结构。我对 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>
包装它。
考虑以下实现,一个方法接受一个 IProgress<int>
,迭代 10000 个对象。 numbers
数组变量 returns 10000 个对象,但 IProgress<int>
仅报告 9970 - 9980 个对象。它因 运行 而异,所以有些人得到 "lost"。
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]);
}
显然我做错了什么,或者我不了解task/threading的内部结构。我对 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>
包装它。