Reputation: 2465
I am using a BackgroundWorker
to send asynchronous HTTP requests (with RestSharp by the way) and need to pass the returned data (HTTP response) to the main thread to update some GUI components based on that data. For this I use ReportProgress
method like this:
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);
}
}
}
This code will raise an exception on ReporProgress
saying that it is illegal to call this method when the Background Worker
has already finished its work and it is absolutely correct because by the time I receive HTTP response the bw_DoWork
will already have finished executing.
So as you can see I am dealing with an asynchronous task in already asynchronous Background Worker thread.
I know that ReportProgress
is marshaled to execute on GUI thread and I don't see any other ways to pass data back to the main form. How can this be fixed/corrected?
Upvotes: 0
Views: 785
Reputation: 802
You need some way to let your background worker sleep/wait until all ExecuteAsync´s are finished. You can do it like this.
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();
}
An alternative would be to not use a BackgroundWorker at all. You can update the UI in WPF using the MainWindow´s dispatcher.
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
// Update UI
});
or
System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(() =>
{
// Update UI
});
Or in Windows Forms using a concrete Form
System.Windows.Forms.Form form = ...;
form.Invoke(new Action( () =>
{
//Update UI
}));
Upvotes: 1