C# AutoResteEvent阻止BackgroundWorker进度报告

C# AutoResteEvent阻止BackgroundWorker进度报告,c#,wpf,progress-bar,backgroundworker,autoresetevent,C#,Wpf,Progress Bar,Backgroundworker,Autoresetevent,我在应用程序中使用BackgroundWorker。当Backgroundworker仍然忙碌时,我可以显示进度条更改。但是,当我使用AutoResteEvent等待Backgroundworker完成时,我看不到进度条发生变化。是否有另一种方法,我可以等待BackgroundWorker完成并释放progressbar更改?我对C#框架和编程相当陌生 private AutoResetEvent _resetEvent = new AutoResetEvent(false); private

我在应用程序中使用BackgroundWorker。当Backgroundworker仍然忙碌时,我可以显示进度条更改。但是,当我使用AutoResteEvent等待Backgroundworker完成时,我看不到进度条发生变化。是否有另一种方法,我可以等待BackgroundWorker完成并释放progressbar更改?我对C#框架和编程相当陌生

private AutoResetEvent _resetEvent = new AutoResetEvent(false);

private void InitializeBackgroundWorker()
        {
            parserBackgroundWorker.DoWork +=
                new DoWorkEventHandler(parserBackgroundWorker_DoWork);
            parserBackgroundWorker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
            parserBackgroundWorker_RunWorkerCompleted);
            parserBackgroundWorker.ProgressChanged +=
                new ProgressChangedEventHandler(
            parserBackgroundWorker_ProgressChanged);
            parserBackgroundWorker.WorkerReportsProgress = true;
            parserBackgroundWorker.WorkerSupportsCancellation = true;
        }

 private void parserBackgroundWorker_DoWork(object sender,
            DoWorkEventArgs e)
        {
            // Get the BackgroundWorker that raised this event.
            BackgroundWorker worker = sender as BackgroundWorker;

            parser.Parse((SegmentFile)e.Argument);
            _resetEvent.Set();
        }

        // This event handler deals with the results of the 
        // background operation. 
        private void parserBackgroundWorker_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
            // First, handle the case where an exception was thrown. 
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }
            else if (e.Cancelled)
            {
                // Next, handle the case where the user canceled  
                // the operation. 
                // Note that due to a race condition in  
                // the DoWork event handler, the Cancelled 
                // flag may not have been set, even though 
                // CancelAsync was called.
                //resultLabel.Text = "Canceled";
            }
            else
            {
                // Finally, handle the case where the operation  
                // succeeded.
                //resultLabel.Text = e.Result.ToString();
            }           
        }

        // This event handler updates the progress bar. 
        private void parserBackgroundWorker_ProgressChanged(object sender,
            ProgressChangedEventArgs e)
        {
            ProgressBar1.Value = e.ProgressPercentage;
        }

parserBackgroundWorker.RunWorkerAsync(selectedSegFile);
// when I comment this code I do see the progress bar change as the thread is doing the work.
_resetEvent.WaitOne();

假设您有以下代码:

parserBackgroundWorker.RunWorkerAsync(selectedSegFile);
_resetEvent.WaitOne();
MessageBox.Show("Work Done");
然后可以将代码放在
\u resetEvent.WaitOne()之后,并将此方法附加到
RunWorkerCompleted
事件,然后删除
\u resetEvent.WaitOne()

您还可以将
委托
作为参数指定给
BackgroundWorker
,并在
parserBackgroundWorker\u RunWorkerCompleted

class ParserWorkerParameters
{
    public String SegFile { get; set; }
    public Action CallBack { get; set; }

    public ParserWorkerParameters(string segFile, Action callBack)
    {
       SegFile = segFile;
       CallBack = callBack;
    }
}

parserBackgroundWorker.RunWorkerAsync(new ParserWorkerParameters("someString", () =>  MessageBox.Show("worker complete")));

private void parserBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    ParserWorkerParameters param = e.Argument as ParserWorkerParameters;
    parser.Parse((SegmentFile)param.SegFile);
    e.Result = param;
}

private void parserBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //old code
    ParserWorkerParameters param = e.Result as ParserWorkerParameters;
    if (param.CallBack != null)
    {
        param.CallBack();
    }
}

正如上面的评论中所讨论的,问题在于调用
WaitOne
方法时阻塞了UI线程。您的
BackgroundWorker
实际上正在更新进度条的
Value
属性(这样做会使其无效),但是应该执行控件实际绘制的线程在等待事件时被阻塞

根据您的评论,您关心的似乎是如何使用不同的参数启动worker,并根据这些参数以不同的方式处理
RunWorkerCompleted
事件

一种方法可能是,每当您从程序中的某个点启动worker时,为该事件附加不同的处理程序:

// attach the handler
parserBackgroundWorker.RunWorkerCompleted += FirstCaseHandler;

// run it
parserBackgroundWorker.RunWorkerAsync(selectedSegFile);
在这种情况下,每个处理程序应该做的第一件事是分离自身:

void FirstCaseHandler(object sender, RunWorkerCompletedEventArgs e)
{
    // detach this specific handler
    parserBackgroundWorker.RunWorkerCompleted -= FirstCaseHandler;

    // do stuff
    ...
}
或者,您可以附加一个处理程序,并根据辅助结果使用它处理不同的情况

在这种情况下,您可以设置
DoWorkEventArgs
Result
属性,以便在执行
DoWork
方法时将结果对象传递给处理程序:

void parserBackgroundWorker_DoWork(object sender,
        DoWorkEventArgs e)
{
     // do stuff
     var parserResult = parser.Parse((SegmentFile)e.Argument);

     // set the Result property with a custom object which 
     // will allow you to know which case you need to handle

     // this can be simply: e.Result = e.Argument;

     // or, you can create an instance of your own class, something like:
     e.Result = new WorkerResult(e.Argument, parserResult);
}
在这种情况下,您将在
RunWorkerCompleted
处理程序中检查
e.Result
的值:

void parserBackgroundWorker_RunWorkerCompleted(
       object sender, RunWorkerCompletedEventArgs e)
{
    var resultInfo = e.Result as WorkerResult; // or whatever

    // do the right thing based on its value
}

您甚至可以传递回调委托作为参数,并从
RunWorkerCompleted
处理程序调用此方法,因此您确实有许多选项。

为什么要阻止UI线程?您应该将代码放在_resetEvent.WaitOne()之后;正如Dmitry所解释的,在RunWorkerCompleted()中,调用
WaitOne
时会阻塞UI线程。您的
BackgroundWorker
正在更新进度条的
属性并使其无效,但是应该执行控件实际绘制的线程在等待事件时被阻止。我在多个位置使用此BackgroundWorker,因此无法将代码放置在RunWorkerCompleted()中@savi:我真的不明白你的程序是如何组织的,但是你可以很容易地从任何地方附加和分离不同的
RunWorkerCompleted
事件处理程序。或者,您可以有一个单独的处理程序,它在不同的情况下会有不同的行为。您当然不想做的事情是阻止UI线程(因为在这种情况下,您根本不需要后台工作程序)“但是,您可以方便地从任意位置附加和分离不同的RunWorkerCompleted事件处理程序。或者,您可以有一个单独的处理程序,它在不同的情况下会有不同的行为。”?+1这实际上是最常用的方法(在启动工作程序时传递回调委托),我只提到了它,但我看到您提供了一个完整的示例。我想我明白了您的意思。让我试试这种方法并检查一下。非常感谢。我喜欢您关于取消订阅FirstCaseMandler的想法,因为它使您的代码行为与OP代码相同,但我花了一些时间来理解这一点。
void parserBackgroundWorker_RunWorkerCompleted(
       object sender, RunWorkerCompletedEventArgs e)
{
    var resultInfo = e.Result as WorkerResult; // or whatever

    // do the right thing based on its value
}