Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
10
Question by CHPedersen · Dec 30, 2011 at 01:40 PM · multithreadingmessaging

Multithreading and messaging

Dear all,

I use multithreading rather extensively in my project for many reasons. Sometimes it's because I don't want a larger piece of work to freezy my rendering, such as saving an extensive amount of data to the harddrive, other times I do it because the nature of the work relies on careful timing that cannot be locked to a specific framerate.

Often, though, something in Unity needs to happen as a result of something that occurred in a separate thread. Like many of you, I am well aware that Unity's API isn't threadsafe and that I can't do anything that changes the renderstate from a separate thread. So, to get around this, I'm using a type of message where the separate thread changes variables which are threadsafe, and then when something visual must happen, it sets a variable which Unity's mainthread checks to see if it needs to react. This pattern is rather dirty. I've coded an example to demonstrate what it looks like:

 using System;
 using UnityEngine;
 using System.Threading;
 
     public class ThreadingExample : MonoBehaviour
     {
         Thread WorkerThread;
         Boolean UnityMustReact = false;
         Boolean SomeCondition = true;
     
         void Start()
         {
             WorkerThread = new Thread(new ThreadStart(DoWork));
             WorkerThread.Start();
         }
     
         void Update()
         {
             if (UnityMustReact) // This is the problem - I must check this variable all the time
             {
                 // ... Do something that uses the work performed by the separate thread, such as using
                 // vectors whose values were set by the separate thread
     
                 // Done reacting, so reset variable
                 UnityMustReact = false;
             }
         }
     
         void DoWork()
         {
             while (SomeCondition)
             {
                 // Do something that needs careful timing or mustn't freeze Unity
                 // Set some variables that Unity uses, for example vectors to draw something
     
                 // Work is done, so I want Unity to react to it
                 UnityMustReact = true;
             }
         }
     }

As you can see, this pattern is less than optimal. It involves constantly checking a boolean to see if it's time to react to changes done by the thread. This is not good, and generally, one would use a coroutine to schedule these kinds of events. But coroutines cannot be started from a separate thread.

So here's the question:

Can you come up with a cleaner way of messaging Unity to tell it to react to a separate thread's work? More specifically, I'm looking for a way to get rid of the polling. I dislike having to check a boolean every frame.

Thanks in advance and happy new year! :)

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

5 Replies

· Add your reply
  • Sort: 
avatar image
19
Best Answer

Answer by CHPedersen · Jul 09, 2012 at 01:24 PM

I had time to return to this some time ago, and decided to post the solution I went for, which is basically what Bunny suggested. :)

I developed a simple task executer into which you can "inject" code. It looks like this:

 using UnityEngine;
 using System;
 using System.Collections;
 using System.Collections.Generic;
 
 public delegate void Task();
 
 public class TaskExecutorScript : MonoBehaviour {
 
     private Queue<Task> TaskQueue = new Queue<Task>();
     private object _queueLock = new object();
 
  // Update is called once per frame
  void Update () {
         lock (_queueLock)
         {
             if (TaskQueue.Count > 0)
                 TaskQueue.Dequeue()();
         }
  }
 
     public void ScheduleTask(Task newTask)
     {
         lock (_queueLock)
         {
             if (TaskQueue.Count < 100)
                 TaskQueue.Enqueue(newTask);
         }
     }
 }

It's pretty simple, and that's why I like it. Basically, it's a queue of task objects. A Task is just an encapsulation around an anonymous function (a delegate). The ScheduleTask method adds a task to the queue, and then in Update, it executes one per frame. This is basic and can be expanded - For example, I gave it a limit of 100 tasks. If not, it's possible to overload Unity, in case a thread runs rampant and loop-adds more tasks than the main thread can execute. I also decided not to empty the entire loop in one frame, because I didn't want to run the risk of stalling the rendering and cause a chop in the graphics, in case many high-cost tasks are in the queue.

You slap this onto an empty GameObject and acquire a reference to it, then, when your thread completes whatever it's doing that needs a reaction, you can add a task like this:

 GameObject taskExecutor = GameObject.Find("NameOfTheGameObjectToWhichTheTaskExecutorScriptIsAttached");
 TaskExecutorScript taskExecutorScript = taskExecutor.GetComponent<TaskExecutorScript>();
 taskExecutorScript.ScheduleTask(new Task(delegate
 {
     // Add whatever Unity API code you want here
     GameObject go = Instantiate(SomePrefab);
     go.transform.position = new Vector3(0, 0, 0);

     // Etc
 }));

This enables you to put code which is totally Unity API into event handlers executed by separate threads, because the Unity API code isn't actually executed by that thread - it gets scheduled for execution by the TaskExecutorScript.

There. I can't think of a cleaner way to do this. Case closed. :)

Comment
Add comment · Show 9 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image MountDoomTeam · Feb 03, 2013 at 09:16 AM 1
Share

I was reading all of this thinking that it was a handy multithreading Scheduler-but the above code is all on the same core! As far as I can see!?

avatar image CHPedersen · Jul 26, 2013 at 09:45 AM 0
Share

There is no guarantee what physical core it will run on. The subsystem's scheduler in hardware totally deter$$anonymous$$utes this for you, unless you specify CPU affinity in Windows. The TaskExecutor here only enables work which involves calling the Unity API to be added to the mainthread from separate threads.

avatar image hexaust · May 13, 2014 at 02:10 PM 0
Share

Can I make ScheduleTask static? So I can access it like TaskExecutor.ScheduleTask?

avatar image CHPedersen · May 13, 2014 at 08:29 PM 0
Share

No, because TaskExecutor is a $$anonymous$$onoBehaviour. It relies on the rendering loop to execute the scheduled tasks, and you can't attach static classes to GameObjects, as components are inherently instanced objects.

But you can make TaskExecutor follow the singleton pattern, so you can access it through TaskExecutor.Instance.ScheduleTask, and then encapsulate the instantiation inside a static property called Instance.

avatar image Emiles · Jun 10, 2014 at 01:45 PM 1
Share

This worked a treat with an issue i encountered making bespokeOSC work in Unity. The OSCServer class uses a thread which meant the oscServer_$$anonymous$$essageReceived handler would not run on the main thread causing havoc when strangeioc signals called up on view mediators to manipulate game objects. Wrapping the handler code in a task and having it run on the update() of a dummy object solved it.

Show more comments
avatar image
3

Answer by Bunny83 · Jan 02, 2012 at 02:50 PM

There are not much alternatives in Unity i guess. It depends on how many of such "events" you have at the same time and how time-critical it is. If it's a visual feedback to a quite long loading process it would be enough to check only 10 or 5 times per sec. So a coroutine which is started together with the thread-task and removed / terminated when the task is finished is the best way i guess.

If you need a lot of different events to be executed as fast as possible you might use a task-queue. Just a List of task objects which is thread-safe so every thread can add new tasks which get executed and removed by the main thread. This way you only have one check each frame. This would be quite similar to a simple custom coroutine implementation like shown here.

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image CHPedersen · Jan 02, 2012 at 03:12 PM 0
Share

I like the idea of a task queue. :) I'm not sure how well it applies to my situation, but it sounds like a neat construct that I can take advantage of in other ways.

$$anonymous$$y scenario involves the reception of, and reaction to, data received through a network socket. It kind of corresponds to a GPS - I want to be able to receive data the moment it is ready, so I can update a position on a map as often as possible, so I have a different thread handle contacting the satelites and parsing the raw GPS data. This is because a separate thread can ask the socket whether data is ready more often than the main thread can render frames. Then, when a new position is ready, I need to move a character in Unity using the new data. (The actual scenario is not a GPS system, but the required solution exhibits the same behavior. ;) )

avatar image CHPedersen · Jul 09, 2012 at 01:04 PM 0
Share

I've returned and accepted this as the right answer - I implemented it recently and liked the result. See update to initial question.

avatar image Bunny83 · Jul 09, 2012 at 01:16 PM 0
Share

It's better you post it as answer and don't put the answer to the question in the question itself. There's also no problem when you accept your own answer.

Do you use a mutex / semaphore / lock to access the queue? I'm not sure, but as far as i know those container classes aren't thread-safe by default. It probably works fine, but there is a chance you get corrupted data if two threads access the same resource at the same time.

avatar image CHPedersen · Jul 09, 2012 at 01:24 PM 0
Share

Alright, I'm moving the answer to a new answer below, then hitting accept on that. (Actually, I did it this way because I didn't wanna take credit - it wasn't my idea, after all).

Good point on the thread safety. It's worked perfectly up to now, but as is always the case with concurrency, there's no guarantee it won't randomly fail in the future. I've added lock statements to the ScheduleTask method and the access that takes place in Update. This should ensure thread safety.

avatar image Bunny83 · Jul 09, 2012 at 01:45 PM 0
Share

The Queue has a Synchronized() function which returns a thread-safe wrapper for a given queue. I think that's the easiest way ;)

avatar image
0

Answer by jef.c · Dec 30, 2011 at 11:45 PM

Could your WorkerThread fire off an event that the other one is subscribed to?

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image CHPedersen · Jan 02, 2012 at 10:05 AM 2
Share

Unfortunately, no. I'm actually already using events to set the boolean, I just omitted that from the example to simplify it.

The reason why is that events are really just a list of callbacks. That's why you use "+=" when you subscribe to an event - you're attaching a function handle to a list of callbacks to be called when the event fires. Therefore, the handler of an event is called by the same thread that fires the event, not the mainthread. Thus, using the Unity API in an event handler attached to an event which is raised by a seperate thread is semantically equivalent to using the Unity API in that same thread.

avatar image
0

Answer by junglemason · Jan 11, 2015 at 09:16 PM

You can also use the EventWaitHandle class to tell the thread to wait until a certain even happens in the main thread. Similar to events, but designed specifically for communicating between threads.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by aeroson · Jul 18, 2015 at 04:53 PM

For my needs i needed to implement ISynchronizeInvoke. Can be used for what you need, and pretty much does what OP is doing now but in more .NET elegant designed way. It even supports return values from another thread.

https://gist.github.com/aeroson/90bf21be3fdc4829e631

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

16 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

[En & Fr] Show a message on the screen - Faire apparaître un message sur l’écran 1 Answer

Thread synchronization and .NET version issues 1 Answer

Defer graphics.blit until render thread? (Android) 1 Answer

Split screen as two processes 0 Answers

Are UnityEngine.Object types still not thread safe? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges