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.
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.
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.
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.
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.