繁体   English   中英

BackgroundWorker ReportProgress 竞争条件

[英]BackgroundWorker ReportProgress race condition

下面的代码是 BackgroundWorker 最基本的例子。

所以基本上有两个线程:一个主线程和一个工作线程。 每当工作线程调用 ReportProgress 时,都会在主线程上调用 bw_ProgressChanged(这是 BackgroundWorker 的工作方式)。

如果工作线程在没有任何延迟的情况下调用 ReportProgress,则主线程不会按顺序处理 bw_ProgressChanged 调用。 有人可以更深入地解释为什么会这样吗?

using System;
using System.ComponentModel;
using System.Threading;

namespace _0042_BackgroundWorkerReportProgress
{
    class Program
    {
        static BackgroundWorker _bw;

        static void Main()
        {
            _bw = new BackgroundWorker
            {
                WorkerReportsProgress = true,
                WorkerSupportsCancellation = true
            };
            _bw.DoWork += bw_DoWork;
            _bw.ProgressChanged += bw_ProgressChanged;
            _bw.RunWorkerCompleted += bw_RunWorkerCompleted;

            _bw.RunWorkerAsync("Hello to worker");

            Console.WriteLine("Press Enter in the next 5 seconds to cancel");
            Console.ReadLine();
            if (_bw.IsBusy) _bw.CancelAsync();
            Console.ReadLine();
        }

        static void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i <= 100; i += 20)
            {
                if (_bw.CancellationPending) { e.Cancel = true; return; }
                _bw.ReportProgress(i);
                Thread.Sleep(1000);  // Without this, there is a race condition.
            }                            
            e.Result = 123;
        }

        static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
                Console.WriteLine("You canceled!");
            else if (e.Error != null)
                Console.WriteLine("Worker exception: " + e.Error.ToString());
            else
                Console.WriteLine("Complete: " + e.Result);
        }

        static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            Console.WriteLine("Reached " + e.ProgressPercentage + "%");
        }
    }
}

BackgroundWorker在调用RunWorkerAsync时捕获当前的SynchronizationContext源代码)。 当调用ReportProgress方法( 源代码)时,它通过发布到先前捕获的SynchronizationContext来调用ProgressChanged事件。 如果没有要捕获的SynchronizationContext.Current (默认情况下发生在控制台应用程序中),则会创建基本SynchronizationContext class 的实例并将其存储为捕获的上下文:

SynchronizationContext class 是一个基础 class,它提供了一个没有同步的自由线程上下文。

这意味着在随机ThreadPool线程中调用ProgressChanged事件。

如果您想在控制台应用程序中使用BackgroundWorker class,并让它以与 WinForms/WPF 应用程序类似的方式运行,则必须安装非自由线程的SynchronizationContext实现。 您可以在此处找到合适的实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM