- Home /
multithread constructor call create gameObject
Hi. When I'm trying to create gameobject in constructor I get the following error:
Internal_CreateGameObject can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
How could I do it so that I could create a gameObject by calling a constructor from multithread using javascript?
I'm making an MMO client side using Unity and that's why I must use multithreading to read data from the server and construct players from the incomingdata.
You need to pass the data loaded in your network thread back to the main thread and create your gameobjects there.
Answer by Bunny83 · May 14, 2015 at 12:37 PM
You can't use any Unity API from another thread. You have to execute those things on the main thread. You can use WhyDoIDoIt's Loom class(i mirrored it on my dropbox since his website is down). Just keep in mind that you have to call Initialize somewhere on the main thread (Start / Awake):
Loom.Initialize();
Now you can simply schedule things on the main thread by passing a delegate to "QueueOnMainThread". You can use lambdas as well.
// inside another thread:
Loom.QueueOnMainThread(()=>{
// Use the Unity API here.
});
Of course you can't talk forth and back between threads like that. The delegate will be invoked in Update when the Loom class iterates the scheduled tasks.
edit
If you need to use the Unity API in another thread and you have to wait for the result you could modify the Loom class to have it return an AutoResetEvent which you signal when the scheduled delegate has been processed in Update.
Passing data can be done most easily with closures (lambda or anonymous delegate). However as said this requires you to have a wait handle (like the AutoResetEvent) since your thread has to wait for the main thread to actually process the delegate.
edit
I've once created an "advanced" version of the Loom class called LoomEx. It handles the queued items more efficient in a linked list and keeps them sorted in the order they should be executed. It also provides a way to wait for the main thread to process the callback before it returns.
Can you elaborate on the modification that must be done to the Loom class in order to make the thread wait for the result? (i'm kind of new to threading so thank you a lot for your help).
Well, that's not explained in a short sentence ^^.
What you basically need is create and store an AutoResetEvent along with the callback for the thread you created. You would then either add the following at the end of the "QueueOn$$anonymous$$ainThread" method or, what would be better, create another method that waits for the signal once it queued the callback.
To block the thread until the callback has been processed on the main thread you would call
yourAutoResetEventInstance.WaitOne();
at the end of the "QueueOn$$anonymous$$ainThread" (or at the end of the new method "QueueOn$$anonymous$$ainThreadWait").
Inside the Update method of the Loom class you would signal the corresponding autoresetevent after the callback has been called by using
yourAutoResetEventInstance.Set();
This will make your worker thread to continue.
It's probably the easiest when you add the AutoResetEvent to the nested "DelayedQueueItem" class. I would also dump the "_actions" list and only use a single List. There is a lot room for improvement as the way the callbacks are processed is not optimal. The calling might be done outside the "lock" but that's another story ^^.
I just quickly did almost a complete rewrite of the Loom class. I created the LoomEx class.
I dropped the List completely and used a simple sorted linked list ins$$anonymous$$d. So each QueueItem simply has a reference to the next item. The items are always sorted by their expiration time. So the first item in the queue is the first to expire. This makes it very easy to deter$$anonymous$$e all items that have expired. It also simplifies adding items without delay which are simply added at the front.
In addition you can now specify to "wait" for the main thread to process the callback. This will block the thread until the main thread has processed the callback.
I created a quick test which looks like this:
LoomEx.RunAsync(() => {
for(int i = 0; i < 5; i++)
{
System.Threading.Thread.Sleep(100);
Debug.Log("Thread work");
}
Debug.Log("queue task on main thread in 2 seconds and wait for processing");
LoomEx.QueueOn$$anonymous$$ainThread(() => {
Debug.Log("Back on main thread");
}, 2, true);
Debug.Log("Thread continue");
});
The console output is:
Thread work
Thread work
Thread work
Thread work
Thread work
queue task on main thread in 2 seconds and wait for processing
Back on main thread
Thread continue
Here "QueueOn$$anonymous$$ainThread" has two additional parameters, the delay in seconds after which the callback should be invoked on the main thread and a boolean that indicates if the method should block the current thread until the callback has been processed.
Note: never use the parameter shouldBlock=true when you call QueueOn$$anonymous$$ainThread actually from the main thread as this would dead-lock your application. Obviously blocking the main thread until the main thread has processed something else won't make much sense ^^. "QueueOn$$anonymous$$ainThread" could check the thread ID to catch accidental misuse.
Hi, Can you please post the Loom class. I cant find it anywhere and I really need it soon
I've fixed the dropbox links (dropbox has disabled the public folder for all users). I've also added a link to my more "advanced" version at the end of my answer.
Answer by minisurma · May 14, 2015 at 06:59 PM
Thank you for great answers. I decided to make following update loop into a main thread script. Well this isn't very efficient way of doing it, but it's good enough for now. :)
function Start(){
InvokeRepeating("UpdatePlayers", 0, 0.25);
}
function UpdatePlayers(){
for(var player : Player in Player.players){
if(player.playerObject == null){
player.playerObject = new GameObject(player.username);
}
}
}
I have to say that that code on its own sets off a few alarm bells for me, not just related to efficiency.
How does the network thread come into play? Is it creating the Player objects and putting them into the Player.players container? Any locking going on?
On the face of it your Player.players container would seem to be quite central to the game so I generally wouldn't want any threads apart from the main one to know anything at all about it.
I tend to think that the best way is for the network thread to do as little as possible beyond its networking responsibilities. Usually in this kind of situation that means doing nothing but download data and pass it on to the main thread which decides what to do with it, acts on that decision, and then disposes of the data (non-blocking queues can be good for this).