Aborting the Thread

We can use the same principle for aborting the thread – we notify the ThreadWorker that it should abort, and allow it to continue until the next yield, before cleanly shutting itself down.

For aborts, it’s often much more important to wait until the thread has stopped running before allowing the main thread to continue. This is because typically you will want to do it when tearing things down (destroying the owning GameObject, exiting the scene, quitting play in editor, etc.) – it’s often the case that after calling Abort() you will destroy resources that the ChildThread is using.

using System.Collections;
using System.Threading;

public class ThreadWorker
{
    private Thread ChildThread = null;
    private EventWaitHandle SuspendHandle = new EventWaitHandle(true, EventResetMode.ManualReset);
    private EventWaitHandle AbortHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
    private bool WantAbort = false;

    public void Start(IEnumerator threadImplementation){/*...*/}
    public void Resume(){/*...*/}

    public void Suspend()
    {
        if(!WantAbort)
            SuspendHandle.Reset();
    }

    public void Abort(bool block=true)
    {
        WantAbort = true;

        Resume();

        if(block)
            AbortHandle.WaitOne();
    }

    private void ThreadLoop(object threadImplementation)
    {
        var impl = threadImplementation as IEnumerator;

        while(!WantAbort && impl.MoveNext())
        {
            if(WantAbort)
                break;

            SuspendHandle.WaitOne();
        }

        AbortHandle.Set();
        ChildThread = null;
    } 
    // etc.

Calling Abort

When Abort() is called, we do the following:

  • Set the WantAbort boolean flag (note boolean assignment is atomic).
  • Resume the thread (as it cannot abort cleanly if blocked)
  • Block on AbortHandle, if requested.

Handling the WantAbort Flag

In the thread loop, WantAbort is checked before and after each step of the Enumerator. If the thread is paused (blocked on SuspendHandle), it will be resumed and the loop will immediately end. Once out of the loop the AbortHandle() is Set, releasing the thread that called Abort().

Additional Notes on Aborting

You definitely want to be explicitly aborting your threads during teardown. Normally this can be done in OnDestroy() or OnApplicationQuit on the MonoBehaviour driving the thread. In these cases you should definitely block. This is also another way in which the enumerator helps a lot – you can block on that abort, knowing that the thread will be cleanly shut down in a reasonable amount of time.

It’s also more than possible to add a similar block option to Suspend(), in case you want to ensure that the thread is inactive before returning to the calling function. Although you might want to consider what happens if Resume() is called whilst you are waiting. I’d probably suggest defining this as invalid behaviour and requiring suspend and resume calls to be done from the same thread.)

Pausing, Resuming and Aborting Threads

Return to Blog