Summary / Thoughts

This is the technique I used to process our flow field updates off the main thread. It’s not the only way to push things onto another thread and there are a few things to bear in mind.

Main Thread Cost

There is still a small amount of main thread cost, made up of:

  • Posting pending changes.
  • Copying changes in.
  • Copying results out.
  • Thread synchronisation.

The bulk of which is thread sync. I’m a little bit surprised by this, as my understanding of EventWaitHandles is that they are simple thread signalling, so maybe there is something faster.

Pipeline Delay

It’s also important to note that this technique adds a pipelining delay of 1 frame. Changes that are queued up in frame x, get updated and the results are available in frame [x+1], but are not actually used until frame [x+2]. For the case of the flow field this is fine as it’s a slowly propagating thing that settles over several frames anyway. This would also be true of most cases.

Garbage Collection

One thing to watch out for is that your copy behaviours are not producing garbage. My pending changes were Lists and the obvious way to copy them across is to simply copy the list reference and create a new List for the main thread. I avoided this by copying the individual elements of the lists and treating them as reusable buffers.

Exiting the Thread

In the example I’ve posted here, I never actually kill off the thread. In the main game, I’ve implemented a slightly clumsy method to allow the thread to exit (there is a bool variable that drives the while loop), but I would like to find a clean way to exit the thread, within Unity’s frameworks.

Thread Priority

I kick off the flow update at the very end of LateUpdate(). This is because it gives very predictable behaviour with regards to the other scripts in the project. However, it does mean that it’s likely to overlap with Rendering and Physics updates, both of which Unity is already Multithreading (on most platforms). It seems that in Unity 5.5 at least, both the main thread and child thread are running with priority set to Lowest, so it would seem that there would be no improvements to be made.

Conclusion

Despite the copy overhead and the frame delay, I like this method. It’s very simple and very clean. Your background tasks get a whole frame to update and the interactions between the threads is limited and contained. In addition, outside of that one copy/sync point, the main thread can interact with its copy of the data without consideration for the multithreadedness of the system.

There is an alternative approach that removes the one-frame delay and the need for copying, but has a number of other drawbacks. I think I’ll write up in a separate post, along with some answers to the questions on how to exiting the thread.

Simple Multithreading for Unity

Return to Blog