Multi-Threading In Unity
A common complaint when using Unity3D is the lack of a thread-safe API. Granted, lots of work has been done to minimize the necessity of a thread-safe environment, but some people can still benefit from having Multi-Threading support within Unity3D.
There are many different problems whose solutions can be found in Multi-Threading. Sometimes it is just unacceptable to process certain complex mathematical algorithms on the main thread. In this instance, it can be very useful to implement a thread safe approach or utilize Compute Shaders.
Using Compute Shaders requires an in depth knowledge of Threading, but also an in depth knowledge of writing shaders. For those that don't want to struggle with multi-threaded gpu approaches to computing, they need look no further than .Net System.Threading.
A piece of code is considered thread-safe if it accesses and manipulates shared data in a way that guarantees safe execution by multiple threads simultaneously. One of the key components to making thread safe code is to avoid Race Conditions within your coding environment.
For example: if you were computing a list of vector positions on another thread while the main thread was reading that list of positions to move units around, you would run into problems. This would be a good example of a bad situation.
If the main thread accesses the same data set as your worker thread, the odds of it reading an out of date piece of data are high. Those odds are just as high as accessing that data while its being written to by the other thread, which would cause an exception. This is called a Race Condition, and is the specific situation we want to avoid.
To actively avoid Race Conditions in our multithreaded code, we will utilize Unity's Coroutine behaviors to manage and keep track of our other threads.
Our Threading Class
Creating Safe Threads
In this example we will manipulate Unity terrain data, generating a random terrain using a perlin noise function, before applying it back in the main thread. To execute this code, we will first create a MultiThreading class that we can use later. Check out the code above.
The gist of what is going on here is that we have created a class that contains virtual functions that we can override on an as needed basis. This will allow us to inherit from this class and create specific worker functions, without needing to create multiple threading classes.
On the left we create our Unity Coroutine function that will manage our thread.
After we start the thread, we yield this function before we attempt to assign or use any data from the thread. Using this method, any other functions on the main thread that need the data will be able to access the data, but until this Coroutine finishes it will be out of date. To avoid this, one could simply avoid accessing the data until the routine is complete.
This is a rough method of implementing Multi-Threading, but it gets the job done and the point across. In a perfect environment, one would create a job system that manages these threads before allowing the data they manipulate to be accessed. This would avoid using out of date data.
Using a perlin noise generator, we are able to calculate a terrain on another thread instead of the main thread. The values I used are bogus values, but the theory and implementation are there for sure.
The key takeaways here are processing information on another thread in Unity, while managing this process from a safe Coroutine before using the data.
If you have any questions or comments please leave them below.