【问题标题】:Background Worker's ReportProgress for an Asynchronous task异步任务的后台 Worker 的 ReportProgress
【发布时间】:2015-04-11 17:52:21
【问题描述】:

我正在使用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 线程上执行,并且我看不到任何其他将数据传递回主窗体的方法。如何解决/纠正这个问题?

【问题讨论】:

  • 不是重点,但如果你让它以这种方式工作,你会看到 5 个报告 i==5 由于捕获。
  • Backgroundworker 在这里没有任何功能(其他 tah ReportProgress),所以放弃它。请改用 aync/await。
  • @HenkHolterman 是的,这就是重点,它应该执行多次。否则我会使用 RunWorkerCompletedEventHandler 来传递数据
  • @HenkHolterman 其实没关系,RunWorkerCompletedEventHandler 会引发同样的异常
  • 我的意思是你的进度值不会是 0,1,2,3,4 而是 5,5,5,5,5。

标签: c# multithreading thread-safety backgroundworker


【解决方案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
    }));

【讨论】:

    猜你喜欢
    • 2022-09-25
    • 1970-01-01
    • 2018-12-26
    • 2016-03-02
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多