Go to Top

Learn when it's the right time to use threads for your game and get to grips with some of the complexities they impose. See how easy it can be to add a multithreaded functions to your game...

Motivation

You should read this article if you are interested in making use of multiple threads for your game. Threading is a fairly complicated topic, but if you master it then there are real benefits for performance when using hardware that has multiple processors or when it is hard to divide processing into manageable chunks.

Threads are great for:

  • Algorithms like A* that need to process a lot of data describing the map of your level
  • Manipulating huge numbers of points, like deforming a mesh
  • Long running uploads to a server
  • Image processing like QR code recognition
Use a thread if you have a lot of processing and little interaction with Unity objects until it is complete, use a coroutine otherwise.

Threads

A thread is a process that runs at the same time as other threads in your program.  On machines with multiple processors then this execution is may be truly simultaneous, depending on whether there are more threads than there are processing cores available.

When programming Unity you always have the main game thread that executes all of the code in your scripts, you also have the option to create extra threads that will run at the same time as the main game.

In Unity you can only access Unity Components, object and Unity system calls from the main thread.  Any attempt to access such items from a second thread will fail and raise errors.  This is a pretty significant limitation and this article will show you ways to work with and around it.

The Myth of Continuity

So when you write code you believe that when a function starts it continues up to the point when it returns unless you do something to move execution to another function - but that isn't what happens.  At any time your code may be just put to sleep by the processor and another piece of code starts running, when the OS decides its time for your code to get some more time, it wakes up and carries on as if nothing had happened.

Your thread can be suspended on any statement in machine code, one high-level language statement will be turned into many machine code statements, so your program could be suspended in the middle of an instruction you wrote.

Now all that is well and good and you don't care.  That is you don't care until you start thinking about threads.  When you have multiple threads they could be simultaneously accessing the same variable or class at the same time, or you could have your code suspended and the value of a variable be changed before the rest of the statement executes.

In this example the first thread is interrupted after loading the value of A into a CPU register ready for the addition of a 1.  The second thread also reads the value of A, still 50, takes 1000 away from it an stores the value in A, at this point A = -950.  Now thread 1 resumes, adds 1 to the 50 it has in the register and stores the result in A.  A now = 51, the -950 has been completely lost.

Fundamentally this makes threads very hard to work with and to make sure that situations like this do not happen you have to take a lot of precautions when accessing variables or memory that might be used by more than one thread at a time.

Unity decided just to avoid the problem with all of their system and framework objects by deeming their access from another thread to be invalid.

Lock Up Your Daughters

So the answer to having problems with variables being concurrently modified is to ensure that only one thread has access to the variable at one time - clearly you want to limit the amount of time that this occurs for as it has effectively meant that you are not allowing multiple threads to work at the same time, sort of defeating the point.

C# has the keyword lock which ensures only one thread can be inside a particular object at a particular time.  I say object because you cannot lock a value type or a primitive.

int a = 50;

object guard = new object();

void ThreadOneCode()
{
     //Some code here

     lock(guard)
     {
         a = a + 1;
     }

     //More code here

}

void ThreadTwoCode()
{
     //Some code here

     lock(guard)
     {
         a = a - 1000;
     }

     //More code here
}

Everything inside the lock(guard) {...} guarantees that only one thread using guard can access it at a time.  You can use any suitable object.

Now you can get into all sorts of problems when you need to lock more than one thing nested inside each other.  You can end up in a circumstance where to get your inner lock, something else needs to happen that cannot, because it relies on getting the same thing as your outer lock (which it can't have because you already have it locked).  This is called deadly embrace - an impossible to break cycle that just freezes everything up.  If you program seriously with threads, this is going to happen to you at some point.

If you are new to threading, try very hard not to write things that share memory or variables if at all possible.  Pass variables to threads, pass them back, but try not to use the same memory.
There are many other ways that threads can be synchronised, but lock is the only one built into the language, all of the others require the construction of system objects to maintain them.  If you  go down that route there are great ways of making efficient code - like locks that allow many things to read a value but only a single thread to own the object when its written to.

Practical Threading in Unity

So in Unity you can only process your own class instances and Unity value types (like Vector3, Quaternion etc) only - as mentioned this makes things a bit limited.  You could though do something like modify the vertices for a mesh on a second thread and then pass the array of Vector3s back to the main thread to actually update the object.

You could write some pathfinding code that uses A* to find a route from some point to another one on your map.

Whatever you do you are going to have to pass things to and from your new thread and the main game thread if you want it to have any real effect!

So how can we write neat code that works across threads without cluttering our classes up with lots of buffers and flags to indicate when things are ready to be processed with all of the incumbent ifs and checks?  It's that clutter that usually leads to bugs and stops people from really using threads efficiently or sometimes at all.

Well using closures and anonymous functions you can build a framework that makes these things easy.  I'm not going to list the whole of that code in this tutorial, but it's available here, but I am going to show you how to use it.

Threads on a Loom

Our class is called Loom.  Loom lets you easily run code on another thread and have that other thread run code on the main game thread when it needs to.

There are only two functions to worry about:

  • RunAsync(Action) which runs a set of statements on another thread
  • QueueOnMainThread(Action, [optional] float time) - which runs a set of statements on the main thread (with an optional delay).

You access Loom using Loom.Current - it deals with creating an invisible game object to interact with the games main thread.

Here's an example of using Loom to update a mesh by multiplying out all of the vertices

	//Scale a mesh on a second thread
	void ScaleMesh(Mesh mesh, float scale)
	{
		//Get the vertices of a mesh
		var vertices = mesh.vertices;
		//Run the action on a new thread
		Loom.RunAsync(()=>{
			//Loop through the vertices
			for(var i = 0; i < vertices.Length; i++)
			{
				//Scale the vertex
				vertices[i] = vertices[i] * scale;
			}
			//Run some code on the main thread
			//to update the mesh
			Loom.QueueOnMainThread(()=>{
				//Set the vertices
				mesh.vertices = vertices;
				//Recalculate the bounds
				mesh.RecalculateBounds();
			});

		});
	}

This is a great example of using closures.  The Action (a method taking no parameters and returning nothing) that is run on the second thread is created using a lambda function (or an anonymous function).  The magic of closures is that these have access to all of the variables in your class and in the parameters and local variables of your function.

You define a lambda function using ()=>{ ... } and everything inside that function is run on a new thread.

When the vertices have been updated we need to modify the mesh on the main thread, so we use QueueOnMainThread which will run during the next time Update cycles are processed (this frame or the next frame depending on when it is called).  QueueOnMainThread also takes an Action so we use exactly the same technique to pass the updated vertices back to the original mesh. Cool!

Source Code

The source code for Loom is available from here.  For those interested in doing the same thing in UnityScript then all you have to do is write:

	//Scale a mesh on a second thread
	function ScaleMesh(mesh : Mesh, scale : float)
	{
		//Get the vertices of a mesh
		var vertices = mesh.vertices;
		//Run the action on a new thread
		Loom.RunAsync(function() {
			//Loop through the vertices
			for(var i = 0; i < vertices.Length; i++)
			{
				//Scale the vertex
				vertices[i] = vertices[i] * scale;
			}
			//Run some code on the main thread
			//to update the mesh
			Loom.QueueOnMainThread(function() {
				//Set the vertices
				mesh.vertices = vertices;
				//Recalculate the bounds
				mesh.RecalculateBounds();
			});

		});
	}
, , , , , , ,

13 Responses to "Threads"

  • Valentin
    November 6, 2012 - 10:40 am Reply

    Good article.
    Have you actually tested what performance increase this method gives?
    I guess all these closures add some boxing/unboxing time.

  • whydoidoit
    November 6, 2012 - 1:11 pm Reply

    Actually closures don’t need to do boxing/unboxing as they turn the variables and parameters of your functions into a compiler generated class which is then automagically merged with your normal class when using the none close variables. That does of course mean you are creating many instances of these classes which will eventually need garbage collection – though I believe the compiler is optimised for this as it never seems to have the size of impact I would be expecting.

  • Xiaoke
    February 11, 2013 - 9:01 am Reply

    Good job~~thanks for share!

    • whydoidoit
      March 27, 2013 - 8:59 am Reply

      Nice! Very interesting results!

  • coAdjoint Tom
    March 30, 2013 - 10:56 am Reply

    This is excellent information, as per usual. If anyone is interested in a video tutorial on threading, I’ve made one here. As well as basic information I also show you how to make a threaded task queue in Unity.

  • Domarius
    April 2, 2013 - 3:06 am Reply

    Amazing! When I got to the bottom of the article, I had to scroll back and re-read the last bit a few times to make sure I wasn’t mistaken; I couldn’t believe you’d done all the work for us and it was that easy – just paste in your code around a section that needs speeding up and boom – threading.

    But it really is! Talk about having multi-threading handed to you on a silver platter. Once you make sure you choose a segment of code that can do some heavy work by itself, and set up the main program to be able to wait an arbitrary amount of frames till that process is done, you’re good! As long as it doesn’t interface with any Unity reference types.

    But Debug.Log works just fine! Which is useful… because if a run-time error happens in the threaded code, no error is thrown – the process just silently stops, and you need to use Log outputs to narrow down where the halt is… helps to make sure that process is working before bundling it up in a thread. But it’s also trivial to remove the thread code around the edges and let it run in the main thread, albeit with a huge lock up while you wait for the process.

    • whydoidoit
      April 2, 2013 - 7:45 am Reply

      Yeah – thankfully you can Log – it was a real relief when I found that out myself :)

      • mrzoom
        April 28, 2013 - 7:40 pm Reply

        Thanks so much! yes print works although yield causes bugs in QueueOnMainThread.

        I have it running on an infinite landscape, it’s really great!!! although i am having trouble for some marching cubes – i put threading codes in the maths points compute function and then no mesh appears, and it causes indexoutofrangeerror with the generic list also:

        function ComputePoints()
        {
        Loom.RunAsync(function() {
        var division: float = 1.0/(size -1);

        for (var z:int = 0; z < size; z++) // z – blue
        for (var y:int = 0; y < size; y++) // y – green
        for (var x:int = 0; x < size; x++) // x – red
        {
        var vx:float = x *division;
        var vy:float = y *division;
        var vz:float = z *division;

        //print("points" + x);
        var value:float;
        value=equation1(vx,vy,vz);
        Loom.QueueOnMainThread(function() {
        values.Add(value);
        points.Add(Vector3(vx,vy,vz));
        });

        }

        //
        });//
        }

  • Water Particles « UnityCoder – Unity3D
    April 13, 2013 - 12:52 am Reply

    [...] TODO: – 3D version? – Add Blobmesh = water? – Optimize..if can.. – Different fluidish materials? (mass, friction, break-ability?) – Sand simulation? (ground digging with excavator..or maybe better using the eulerian water sim grid?) – Test threading? http://unitygems.com/threads/ [...]

  • mrzoom
    April 28, 2013 - 9:19 pm Reply

    Can Loom.RunAsync be run on its own excluding QueueOnMain? what order executions can cause bugs using the loom routines?

  • FireStarter
    May 11, 2013 - 11:38 am Reply

    How do I actually put the file available for download in the project? What is the file type?

  • whydoidoit
    May 14, 2013 - 7:03 am Reply

    It’s a unitypackage so either double click it or use Assets/Import Package/Custom Package

Leave a Reply