后台工作者的ReportProgress用于异步任务

时间:2015-04-11 17:52:22

标签: c# multithreading thread-safety backgroundworker

我使用BackgroundWorker发送异步HTTP请求(顺便说一下,使用RestSharp),并且需要将返回的数据(HTTP响应)传递给主线程,以根据该数据更新某些GUI组件。为此,我使用ReportProgress方法,如下所示:

static void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    Object[] args = (Object[])e.Argument;

    string strURL = args[0].ToString();
    string strParam = args[1].ToString();

    for (int i=0; i<5; i++) {

        var client = new RestClient(strURL);
        var request = new RestRequest(strURL, Method.POST);

        request.AddParameter("someparam", strParam);        

        client.ExecuteAsync(request, response =>
        {
            string strRet = response.Content;
            worker.ReportProgress(i, strArr);
        } 

    }

}

此代码将引发ReporProgress上的异常,说当Background Worker已经完成其工作时调用此方法是非法的,并且它绝对正确,因为当我收到HTTP响应时bw_DoWork已经完成执行。 因此,您可以看到我正在处理已经异步的后台工作线程中的异步任务。 我知道ReportProgress被封送在GUI线程上执行,我没有看到任何其他方法将数据传回主表单。如何修复/纠正?

1 个答案:

答案 0 :(得分:1)

你需要一些方法让你的后台工作人员睡觉/等待,直到所有的ExecuteAsync都完成。 你可以这样做。

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;

    Object[] args = (Object[])e.Argument;

    string strURL = args[0].ToString();
    string strParam = args[1].ToString();

    int finishedCounter = 0;

    // A reset event that is set from the last ExecuteAsync
    AutoResetEvent finishedEvent = new AutoResetEvent(false);
    for (int i=0; i<5; i++) {     

        var client = new RestClient(strURL);
        var request = new RestRequest(strURL, Method.POST);

        request.AddParameter("someparam", strParam); 

        client.ExecuteAsync(request, response =>
        {
            string strRet = response.Content;
            worker.ReportProgress(i, strArr);

            // Check if this is the last worker/step then
            // wake up the background worker to finish the do_work method
            if (Interlocked.Increment(ref finishedCounter) == 5) 
                finishedEvent.Set();    
        });
    }

    // wait till work is done
    finishedEvent.WaitOne();
}

另一种方法是根本不使用BackgroundWorker。 您可以使用MainWindow的调度程序更新WPF中的UI。

System.Windows.Application.Current.Dispatcher.Invoke(() =>
    {
        // Update UI
    });

or 

System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(() =>
    {
        // Update UI
    });

或使用具体表单的Windows窗体

System.Windows.Forms.Form form = ...;

form.Invoke(new Action( () =>
    {
        //Update UI
    }));