We’re using threads to do some heavy computation in the background. We know all about the Unity API not being thread safe so you don’t have to worry about that
Does anyone have some good practices on how to do multithreading? Because we’ve just started and already found some major pitfalls:
Exceptions on threads are ignored. There’s not even a log message! We’re going to have to write our own thread exception handling just because Unity is dumb.
Threads do not pause when you hit ‘pause’. This was a bit of a shocker. Why are the threads not suspended? How can we detect that the game has been paused in the editor?
Threads do not end when you stop running the game. This is just mind-boggling. What? so threads aren’t cleaned up resulting in all sorts of memory leaking every time a thread doesn’t clean up properly? If a thread gets stuck in an infinite loop you’ll just have to exit Unity entirely? How does anyone do development on a threaded application in this scenario?
I’m certain I’m not the first to notice this, so can anyone give me some pointers as to how to approach this?
Spawned threads don’t report unhandled exceptions in the .Net framework unless you explicitly choose to handle them one way or another. This makes sense as your main thread may well be mid-method when the exception is raised.
The simplest way to catch all Exceptions is to launch a thread as follows:
Thread t = new Thread(() => {
try {
//Call your Work() method
} catch(Exception e) {
//Log
}
});
t.Start();
There are also other places you can hook into eg the AppDomain but they’re less explicit.
As an aside, you should always write exception handling for your threads!
Re: Not pausing… When you Pause() Unity just stops calling certain methods on your MonoBehaviours. It has nothing to do with actually pausing any code execution. Suspending all background threads would cause other problems (eg music halting).
If you retain a reference to your threads, you can work around this easily.
Old-school, use t.Suspend() and t.Resume() as required. A better way is to use a ManualResetEvent and check its status. This allows your current iteration of the background thread to complete before pausing. Example here
An application exits when all foreground threads have finished execution.
.Net doesn’t know what you’re using the thread for. It might be writing to disk and aborting mid-method could result in corruption.
The problem here is that you haven’t told .Net that you’re spawning a background thread that can be aborted safely.
Thread t = ...;
t.IsBackground = true;
t.Start();
So, really, there’s nothing wrong with the threading model, but you may benefit from a little more reading up on the complexities of multi-threaded code. It’s a complex subject and probably the second hardest aspect of programming to get right after cryptography.