How to Make Backgroundworker Return an Object

BackgroundWorker Return A Value?

Subscribe to the RunWorkerCompleted event. That event contains the return value of the background operation.

Of course, that value would be returned from inside the DoWorkEventHandler, like so:

b.DoWork += new DoWorkEventHandler(
delegate(object sender, DoWorkEventArgs e) {
double returnValue = 0;
for(int i=0;i<100;i++){
returnValue += (i+1);
}
e.Result = returnValue;
}
);

C# BackgroundWorker return value after finishing

It appears you want to run a async code inside a synchronous method. I.e. you have a method which returns a SocketResponse, and want to make it asynchronous.

Well, here's the problem. Basically - you can't. Or at least it's not as simple as just making use of BackgroundWorker.

One way to do this is to also employ a TaskCompletionSource, and then wait for it to finish. The code would look like this:

public override SockResponse Process(Socket socket, Client client)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(
delegate (object sender, DoWorkEventArgs e)
{
CallResponse rsp = new CallResponse();
try
{
using (Transact X = client.Session.NewTransaction())
{
foreach (CallData call in Payload)
{
DataCall dataCall = new DataCall();
SqlDataReader rdr = dataCall.Execute(X, call);
rsp.Result.Add(dataCall.BuildResult(rdr));
rdr.Close();
}
rsp.ErrMsg = "";
X.Commit();
}
}
catch (Exception err)
{
rsp.ErrMsg = err.Message;
e.Result = rsp;
}
e.Result = rsp;
});
TaskCompletionSource<SockResponse> tcs = new TaskCompletionSource<SockResponse>();
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
delegate (object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
Log.Logger.Error(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.
Log.Logger.Info("CALL process was cancelled");
}
else
{
// Then, handle the result
tcs.SetResult(e.Result);
}
});
worker.RunWorkerAsync();
return tcs.Task.Result;
}

This is an ugly approach - your Process method might be running code in a separate thread, but will otherwise take a long time to execute. The use of BackgroundWorker in this case is also overkill (a simple Task would work just the same in this case). A simplified version of the code using a Task would be:

public override SockResponse Process(Socket socket, Client client)
{
return Task.Factory.StartNew(() =>
{
CallResponse rsp = new CallResponse();
try
{
using (Transact X = client.Session.NewTransaction())
{
foreach (CallData call in Payload)
{
DataCall dataCall = new DataCall();
SqlDataReader rdr = dataCall.Execute(X, call);
rsp.Result.Add(dataCall.BuildResult(rdr));
rdr.Close();
}
rsp.ErrMsg = "";
X.Commit();
}
}
catch (Exception err)
{
rsp.ErrMsg = err.Message;
return rsp;
}
return rsp;
}).Result;
}

The above is running code in a new thread, but the Process method will still take a lot of time.

A BETTER approach is to make use of the async/await keywords and rewrite the method to return a Task. This MIGHT require that you rewrite your application in more places than one.

Here's how that same method might look like as an async method:

public async Task<SockResponse> ProcessAsync(Socket socket, Client client)
{
var task = Task.Factory.StartNew(() =>
{
CallResponse rsp = new CallResponse();
try
{
using (Transact X = client.Session.NewTransaction())
{
foreach (CallData call in Payload)
{
DataCall dataCall = new DataCall();
SqlDataReader rdr = dataCall.Execute(X, call);
rsp.Result.Add(dataCall.BuildResult(rdr));
rdr.Close();
}
rsp.ErrMsg = "";
X.Commit();
}
}
catch (Exception err)
{
rsp.ErrMsg = err.Message;
return rsp;
}
return rsp;
});
return await task;
}

HOWEVER, I already see potential issues with this: your Process method is an override, meaning you can't just make it return a Task, and you can't just slap on the async keyword on it either.

So, depending on how you're running the Process method, perhaps you can simply run that in a separate thread? Something along the lines of:

var socketResponse = await Task.Factory.StartNew(() => Process(socket, client));

or (if not using async/await)

var socketResponse = Task.Factory.StartNew(() => Process(socket, client)).Result;

Also, unless you're overriding your own base class (meaning you can rewrite code to make it async), then it seems like you're working with sockets - code related to Socket use is, by default, based on events (Begin/End methods), and should be naturally "asynchronous".

Still not getting how to return a value from BackgroundWorker

Short answer: you can't.


Think about what it means to return the value back to the method that called RunWorkerAsync. This would mean that the method has to pause its execution, wait for the background worker to complete its work, then continue again. But you still want other methods to run during the waiting, otherwise the UI would freeze.

Does that sound familiar to you? That's basically what an async method does when it encounters an await expression!

BackgroundWorker is quite an old API, and there wasn't the fancy feature of async/await when it came out (async/await is a language feature anyway), which is why it uses events to do the trick. Back then, there wasn't a way to do an operation asynchronously, and return a value back to the caller elegantly.

Now that we have async/await, you can use it to do what you want:

someReuslt = await Task.Run(() => { ... });

If you haven't heard about async/await at all, this is a good place to start.

Backgroundworker to return a string[] array

If I understand your question correctly then you can set e.Result in backgroundWorker1_doWork() event handler with the string[] array and then use it in RunWorkerCompleted event handler.

The following is the code which I have written in winforms.

    BackgroundWorker work = null;
private void button1_Click(object sender, EventArgs e)
{
work = new BackgroundWorker();
work.DoWork += new DoWorkEventHandler(work_DoWork);
work.RunWorkerCompleted += new RunWorkerCompletedEventHandler(work_RunWorkerCompleted);
work.RunWorkerAsync();
}

void work_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
string[] arr = (e.Result as string[]);

foreach (var item in arr)
{
MessageBox.Show(item);
}
}

void work_DoWork(object sender, DoWorkEventArgs e)
{
string[] arr = new string[3];
arr[0] = "aa";
arr[1] = "bb";
arr[2] = "cc";

e.Result = arr;
}

Hope this helps

Return an Object from BackgroundWorker

Call the Freeze() function on the BitmapSource when you are still on the background thread.

This makes the object immutable, and can therefore be used on the main thread.

Learn here more about freezable wpf objects. The page discusses a lot of other aspects of Freezables, but it also explicitly states: "A frozen Freezable can also be shared across threads, ...". This ability built in WPF to build GUI elements (such as bitmaps) on background threads is the #1 underadvertized WPF feature if you ask me.

How to return data from a BackgroundWorker in c++?

You're near. Datapacket is not a .NET reference type (class) and I'd assume you do not want to dispose it when it's out of scope. Change it to ref class and just add ^ in your backgroundWorker_DoWork (and change . with -> where appropriate):

backgroundWorker_DoWork(Object^  sender,DoWorkEventArgs^  e) 
{
Datapacket^ dp = gcnew Datapacket();
dp->Val1 = 5;
dp->Val2 = 34.6;

backgroundWorker->ReportProgress(0, dp);
}

In your event handler you just need to cast UserState property to original type (and change . with -> where appropriate):

backgroundWorker_ProgressChanged(Object^  sender, ProgressChangedEventArgs^  e)
{
Datapacket^ dp = dynamic_cast<Datapacket^>(e->UserState);

// update gui, etc
textBox_Data->Text = dp->Val1.ToString();
}

Do not forget to set WorkerReportsProgress to true.

How to return multiple result from backgroundworker C#

Create a data transfer type. For example:

class MyResult //Name the classes and properties accordingly
{
public string[] Strings {get; set;}
public double[] Doubles {get; set;}
}

private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
//Do work..
e.Result = new MyResult {Strings =..., Doubles = ... };
}

private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MyResult result = (MyResult)e.Result;
//Do whatever with result
}


Related Topics



Leave a reply



Submit