后台工作者 RunWorkCompleted 事件

Background worker RunWorkCompleted Event

我有以下 wpf 程序,我想做的是 btnAnalyzer_click 等待 DoWork 和 RunWorkerCompleted 方法完成的方法。所以我使用了 AutoResetEvent,但现在 bwAnalyze_click 方法(#4 行)运行 在 DoWork 之后,然后是 WorkerCompleted 方法(行顺序 - #1 #2 #4 & #3)。但我希望他们按照#1 #2 #3 & #4 的顺序执行。有什么解决方案或建议吗?

public partial class MainWindow : Window
{
    private readonly BackgroundWorker bwAnalyzer = new BackgroundWorker();
    private AutoResetEvent autoReset;//to signal the end of the BackgroudnWork

    public MainWindow()
    {
        InitializeComponent();
        autoReset = new AutoResetEvent(false);
        bwAnalyzer.DoWork += new DoWorkEventHandler(DoWork);
        bwAnalyzer.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerCompleted);
    }

    void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Console.WriteLine("Completed"); #3
        autoReset.Set();
    }

    void DoWork(object sender, DoWorkEventArgs e)
    {
        Console.WriteLine("load"); #2

    }

    private void btnAnalyze_Click(object sender, RoutedEventArgs e)
    {
        bwAnalyzer.RunWorkerAsync(); #1
        autoReset.WaitOne();//when commented working properly
        Console.WriteLine("click"); #4
    }
}

使用任务,并使用 ContinueWith 方法:https://msdn.microsoft.com/en-us/library/dd270696(v=vs.110).aspx

我想下面应该给出所需的行为 -

 private void btnAnalyze_Click(object sender, RoutedEventArgs e)
 {      

       Task.Factory.StartNew(() => {   

       })
       .ContinueWith(f => {            

       })
       .Wait(); 
 }

虽然建议使用 Tasks 的答案会起作用,但我有点困惑为什么要等到后台工作人员完成其工作,然后再让 UI 线程的点击事件继续。据我了解,您使用后台工作者是因为您想在不阻塞 UI 的情况下执行后台工作。你问问题的方式表明你想阻止 UI 直到你的工作完成。既然如此,为什么还要使用单独的线程?

此外,关于您的评论:

autoReset.WaitOne();//when commented working properly

我假设如果您不对它进行注释,UI 会永久阻塞吗?当您在按钮的点击事件中调用 autoReset.WaitOne() 时,您将阻塞 UI 线程。 DoWork 将在后台线程上 运行,而 RunWorkerCompleted 事件 运行 在 UI 线程上。所以 RunWorkerCompleted 永远不会执行。

如果您对 WaitOne() 调用发表评论,我认为它根本不会起作用 "properly"。我测试了它,由于 UI 线程和后台线程之间的竞争,它在 click->load->completed 和 load->click->completed 之间变化。

这里的大部分答案都正确地要求您尝试使用 TPL 做您想做的事情,但是如果您遇到需要使用 ManualResetEvent/AutoResetEvent 的复杂场景,那么这里就是您问题的解决方案. 只需将您的 btn click 逻辑包围在任务或线程中。这个想法是您不想保留 UI(STA Main) 线程。这会导致一些运行时优化并导致意外行为。

Task.Factory.StartNew(() =>
            {
                bwAnalyzer.RunWorkerAsync(); //#1
                autoReset.WaitOne(); //when commented working properly
                Console.WriteLine("click"); //#4
            });

只要有上面的代码片段就可以给你想要的行为。