Unity + multiple threads = nope?

Multithreading is an ability of processor to execute multiple threads concurently. Each thread execute sequence of programming instructions. So in theory you can achieve better performace of your game by divide more complex operations to smaller pieces and run them in separate threads. In theory ;) One time I worked on item browser in our AR application. This browser cointained a lot of elements loaded from server. We occured a problem of few seconds application freeze when data was downloading from web. I had to find a solution to solve this problem, so I tought that usage of the threads it's a brilliant idea. What surprised me when I saw this:
So the question is, is it possible to using a threads in Unity?

Threads in Unity

Fortunately you can use multi threads in Unity at all. You only can't use Unity API methods themselves across different threads, as they are not thread-safe, but that won't prevent you from doing background processing tasks. Internally Unity does a lot of multi threading and sheduling stuff, but game level code can't use multiple threads because when engine does gameworld updates it is in a known and valid state. You will occur unexpected behaviours when you will try change a particle system during a threaded particle system update, for example. Because Unity use an older version of .NET you can't use newer features and libraries of threading too. What is more Random and Time static members are not available, so you should use instance of System.Random per thread if you need randomness, and System.Diagnostics.Stopwatch if you need timing info.

Without any problems you can use threads with Mathf functions, Vector, Matrix, Quaternion, and Color structs - all work fine across threads, so you can do most of your computations separately. The areas where threads can still be useful:

  • AI
  • Pathfinding
  • Network communication
  • Files operations

Simple example of using threads in Unity

With simple example below I'll show you how to use multi threads in Unity. Firstly let's create an empty project and put cube within scene. Attach a new Move.cs script to this object.

Put this simple code in script:

bool goingLeft;

private void Update()  
{
    MoveObject();
}

void MoveObject()  
{
   transform.localPosition += Vector3.right * (goingLeft ? -.01f : .01f);

   if ((goingLeft && transform.localPosition.x < -3) ||
      (!goingLeft && transform.localPosition.x > 3))
            goingLeft = !goingLeft;
}

When you start the game, you will see cube continously moving from the right side to the left side. We are going to run MoveObject() method in separate thread. So remove Update() function and add this block of code:

private void Start()  
{
   Thread thread = new Thread(Run);
   thread.Start();
}

void Run()  
{
   while (true)
   {
      MoveObject();
   }
}

Click play and... nothing happens. As I mentioned earlier you can't use threads with elements like transform. You need to separate threads-friendly computations from Unity API. Add variable Vector3 lastPosition and at Start() assign value of transform.localPosition to this variable. Now we can calculate new position in separate thread and then assign it to transform in Update() function.

void MoveObject()  
{
   lastPosition += Vector3.right * (goingLeft ? -.01f : .01f);
   if ((goingLeft && lastPosition.x < -3) || (!goingLeft && lastPosition.x > 3))
      goingLeft = !goingLeft;
}

private void Update()  
{
   transform.localPosition = lastPosition;
}

As you can see now we get rid of "get_transform can only be called from the main thread." error. However cube moves chaotically. Sorry about that but now when you stop the game and try run it again your Unity will freeze :P To resolve this issue you should terminate a thread when you exit your game. Replace while(true) with while(!isStopped) and add this block of code:

bool isStopped;

private void OnDestroy()  
{
   thread.Abort();
   isStopped = true;
}

private void OnApplicationQuit()  
{
   thread.Abort();
   isStopped = true;
}

Now we also need to synchronise thread with Update() to remove crazy jumping of our object. In this purpose we will use AutoResetEvent object. Add initiation of this object to Start() method:

resetEvent = new AutoresetEvent(false);

And modify rest of functions this way:

void Run()  
{
   while (!isStopped)
   {
      time = DateTime.Now;
      MoveObject();
   }
}

void MoveObject()  
{
   resetEvent.WaitOne();
   DateTime now = DateTime.Now;
   TimeSpan deltaTime = now - time;
   time = now;
   lastPosition += Vector3.right * (goingLeft ? -1f : 1f) 
      * (float)deltaTime.TotalSeconds;

   if ((goingLeft && lastPosition.x < -3) || (!goingLeft && lastPosition.x > 3))
      goingLeft = !goingLeft;
}

private void Update()  
{
   resetEvent.Set();
   transform.localPosition = lastPosition;
}

To keep stable move speed of object we should multiply new position by Time.deltaTime. Unfortunately we can't use it outside main thread too. So we need to manually compute deltaTime within thread loop. Now cube moves perfectly and we do it using threads!

Conclusion

Using threads in game engine is tricky. Unity's Api is NOT thread safe so it can't be used from another thread. Unity have also implemented a thread-check to prevent you from trying to use it from another thread. Some equivalent of using threads are Coroutines. They implement an cooperative multitasking system. I will tell you about them another time :).

You can use your own threads for calculating something in the background: AI, pathfinding etc. However you can still mix Unity stuff with multithreading computations but don't forget to synchronize it! ;)

I hope it’s useful for you. If you have any comments or questions, please write them here!

Mateusz Olszewski - Lead Programmer



Show Comments