如何在异步函数中从循环报告进度中消除竞争条件?

Agr*_*rim 1 c# asynchronous async-await

我试图根据基于任务的异步模式报告异步函数的进度.

using System;
using System.Threading.Tasks;

namespace TestConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            Blah();
            Console.ReadLine();
        }

        static async void Blah()
        {
            var ProgressReporter = new Progress<int>(i => Console.WriteLine(i + "%"));
            await Foo(ProgressReporter);
        }

        static Task Foo(IProgress<int> onProgressPercentChanged)
        {
            return Task.Run(() =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    if (i % 100 == 0)
                    {
                        onProgressPercentChanged.Report(i / 100);
                    }
                }
            });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,随着i不断更新,在打印之前,输出会混乱.示例输出:

1%
3%
0%
4%
5%
6%
8%
7%
9%
2%
Run Code Online (Sandbox Code Playgroud)

我的信念是IProgress接口旨在通过等待更新进度时要执行的任何lambda表达式或方法来消除此类竞争条件.我错过了任何可以消除这种竞争条件的东西吗?

Sim*_*ead 7

这是因为回调正在一个线程上运行.

为什么?

控制台应用程序.

根据ProgressMSDN类文档:

向构造函数提供的任何处理程序或使用ProgressChanged事件注册的事件处理程序都是通过构造实例时捕获的SynchronizationContext实例调用的.如果在构造时没有当前的SynchronizationContext,则将在ThreadPool上调用回调.

从本质上讲,你的回调是不成功的,因为SynchronizationContext在Console应用程序中没有.所以它们在ThreadPool线程上被触发.

在Windows应用程序中尝试..这是完全相同代码的输出的屏幕截图:

同步示例