If you’re an experienced developer then you most probably worked with threads at some point of your career. As you’re now working with Unity, you most probably have forgotten about how the threads were once important to you.
Unity as a game engine handles many things for you. It also introduces a functionality called Coroutines that can be threads’ substitution in most cases. Yet if you use coroutines, this code still works on a single thread so you don’t need to worry about concurrency issues. One may ask – “So why do I need threads when I can use Coroutines instead?”. It depends…
When threads are bad
You have to realize that creating a thread is an expensive operation. You use a thread poll design pattern, but there’s the moment when you have to synchronize back your computed data to the main thread. This is very individual thing, so you have to consider this very carefully, maybe even doing some performance tests, because synchronization might be more expensive operation than computing your data in the main thread in the first place.
Threads are dangerous, so you have to be very careful when synchronizing things back. You also need to remember that Unity API is not thread safe, so all calls to Unity API should be done from the main thread.
When threads are good
When you’re computing some expensive and/or long-term operations. Like:
- AI
- Pathfinding
- Network communication
- Files operations
Sometimes you don’t have other choice than using a thread. Nobody wants their game to freeze for a few seconds. Threads are often created to listen for events or handle I/O. These things should be handled as fast as possible.
Minimizing the risk
You have to know about the risk. Threads can be very, very risky, because if not used with caution they can crash your game without a word of warning. What’s more, these kind of errors are very difficult to trace down, because everything may work fine for most of the time just to get you by surprise when you least expect it.
There are some techniques and libraries that you should know to minimize the risk of using threads.
The documentation
This is where you should start. You have to realize how difficult threads are and what are the basic concepts.
Use UnityToolbag Dispatcher
Dispatcher is a class that is included in UnityToolbag library. It checks if the code passed to dispatcher is always executed in the main thread, so you can safely execute Unity API code within.
// this code is executed in separate thread float computedHealth = 5; Dispatcher.Invoke(() => { // this code is executed in main thread var player = GameObject.FindByTag("Player"); player.health = computedHealth; });
Use UnityToolbag Futures
Future is another class from UnityToolbag. It depends on the Dispatcher, so you will have to install both.
Future is a reversed case of the above example. Before, we made computations in a thread and then sent the data back to the main thread. But the Future is a nice interface for getting the data back from a thread. Here’s an example:
private Future<float> ComputeHealth() { Future<float> future = new Future<float>(); // this will use a thread from a thread pool future.Process(() => { return 5; }) return future; } // called by Unity void Start() { Future<float> futureHealth = ComputeHealth(); futureHealth.OnSuccess((health) => player.health = health); }
More to read
I don’t want to repeat myself when there are many great articles about threads. You know the risks now and you know what you may gain when using thread. Now, go and learn the concepts. Here are some resources that I can recommend.