Using BackgroundWorker with Unity, Completed not running on Main thread ?
I'm working on an app that communicates heavily with an API. In order to get the data from the API the app first has to parse the whole JSON it receives. I'm looking into moving the parsing part in a worker thread so it doesn't create hiccups in the UI.
Now I know Unity's API is not thread safe. But I've looked at the BackgroundWorker class which is supposed to run a work function on a separate worker thread and a completed function that should run on the same thread the worker was created on.
But it turns out the completed function won't run on the main thread, it is actually running on the same worker thread which is definitely what I'm trying to avoid/bypass.
I'm testing this on a case where I have my ChallengesManager polling the server for a list of challenges, once received, I start the worked thread to parse and register the challenges in a Dictionnary>
void OnChallengesLoaded(APIResponse r, Hashtable args){
ResetChallengesList();
Debug.Log("Current thread [Main] : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
InitWorker();
JSONTask t = new JSONTask();
t.toParse = r.body;
bWorker.RunWorkerAsync(t);
Debug.Log("Current thread [AfterStartBackground] : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
}
Then I have those two functions to do the work and for after it's done :
void Worker_DoWork(object sender, DoWorkEventArgs e){
Debug.Log("Starting work in background");
JSONTask t = (JSONTask) e.Argument;
Debug.Log("Current thread [Work] : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
e.Result = t.Parse();
}
void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){
Debug.Log("Work done !");
JSONNode node = (JSONNode) e.Result;
if(node != null){
RegisterChallenges(node, "toPlay");
RegisterChallenges(node, "waitingForOpponent");
RegisterChallenges(node, "finished");
RegisterChallenges(node, "toBeReviewed");
}
Debug.Log("Challenges retrieved ! Waiting : " + challenges["waitingForOpponent"].Count + " Ready : " + challenges["toPlay"].Count + " Finished : " + challenges["finished"].Count);
Debug.Log("Current thread [Completed] : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
/*if(OnChallengesRetrieved != null){
OnChallengesRetrieved(challenges);
}*/
}
Then I have this function for initializing the worker :
void InitWorker(){
Debug.Log("Current thread [InitWorker] : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
bWorker = new BackgroundWorker();
bWorker.DoWork += new DoWorkEventHandler(Worker_DoWork);
bWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
}
And I also have this JSONTask class to encapsulate the parsing task :
public class JSONTask {
public string toParse;
public JSONNode Parse(){
return JSON.Parse(toParse);
}
}
Now the output of this is not really what I expect :
Current thread [Main] : 1
Current thread [InitWorker] : 1
Current thread [Work] : 5
Current thread [AfterStartBackground] : 1
Challenges retrieved ! Waiting : 18 Ready : 5 Finished : 5
Current thread [Completed] : 2
So the completed is not back on the main thread, and it's not even on the worker thread either. If I launch this whole process once more I get the completed on the same worker thread but still not going back to main thread.
Everything I read about background workers says it should go back to the thread it was created in, but that is simply not the case here and I have no idea why. Please help me :D
Answer by Dave-Carlile · Sep 03, 2015 at 05:54 PM
It sounds like there may be issues using BackgroundWorkers when a Form isn't available. Looking at the comments under the documentation for the 2.0 framework:
If you have a class that uses BackgroundWorker objects and want to do unit tests on the class using VS unit testing framework - you need to instantiate a Form object - otherwise the RunWorkerComplete event will happen in random threads as opposed to the expected main thread.
That's in the context of unit testing, but I would imagine that would be an issue in any case where you don't have the Form object.
That seems to be the problem yes, what can I use ins$$anonymous$$d ? Tasks ?
I'm not sure if those are in the 2.0 framework?
Another option is to find an implementation of a thread safe queue. Just use a normal thread and when it's done create some object instance to hold the results and add it to the queue. In some Update method, periodically pull items from the queue and do what you need to with the results.
Your answer
Follow this Question
Related Questions
How to integrate threading with a www query for a UWP app? 1 Answer
Raycasting at regular intervals with a frequency above the framerate. 0 Answers
Calling back to main thread from editor script 0 Answers
FixedUpdate logic step by step 1 Answer
Given volume dimensions and direction, how to determine if Vector3 is contained 2 Answers