我创建并运行这样的任务:
var task = new Task(WorkWithResult, cts.Token); //cts.Token - маркер отмены, но это не важно
task.Start();
WorkWithResult 中使用的变量:
var result = new int[N];
string stringResult = string.Empty;
我正在等待任务以标准方式完成,如下所示:
await task.ConfigureAwait(true);
WorkWithResult 是一个本地函数,如下所示:
void WorkWithResult()
{
var sbResult = new StringBuilder(N * 5);
var progressValue = 0.0;
var progressStep = 100d / N / 2;
result[0] = PrimeNumbers.Next_prime(0);
progress.Report((int) (progressValue += progressStep));
for (var i = 0; i < result.Length - 1; i++)
{
result[i + 1] = PrimeNumbers.Next_prime(result[i]);
cts.Token
.ThrowIfCancellationRequested();
progress.Report((int) (progressValue += progressStep));
}
foreach (var number in result)
{ // Блокировка UI. Почему?
sbResult.AppendLine(number.ToString());
cts.Token
.ThrowIfCancellationRequested();
progress.Report((int) (progressValue += progressStep));
}
stringResult = sbResult.ToString();
}
在最后一个循环的执行过程中,出现了一个 UI 阻塞(一个从 100,000 开始的数字数组,阻塞 1 秒)。为什么?代码显然在不同的线程上运行,但我通过Task. 这个周期与第 1 周期的不同之处在于sbResult.AppendLine. result我认为这可能是在与正在运行的线程不同的线程上创建的数组访问WorkWithResult,但在循环 1 中不会发生阻塞。
为什么会发生阻塞,如何避免呢?
在第一个周期中,您发送的报告之间存在一些延迟(延迟是由于难以找到素数)。正是在此时间间隔内,UI 设法绘制、响应用户操作等。
在第二个周期中,您会不停地对新报告进行脱粒,因为它的
AppendLine工作速度非常快!(甚至比发送报告还要快)因此,UI 线程专门忙于处理报告,除了处理报告之外没有时间做任何事情。在可能的修复中,最简单的方法是
progress.Report从第二个周期中删除。另一种选择是发送每 (N/50) 个报告,并跳过其余的报告(如果您需要精确到百分之十分之一,则每 (N/500) 个)。
另一种选择是跟踪运行时间并每半秒发送一次报告。