SafeMonoBehaviour

Here’s a helper class for those who wants to use threads in combination with Unity objects.

This class adds a function named Invoke(delegate), which will invoke the specified function in the main thread.

All you need to to is to extend SafeMonoBehaviour instead of MonoBehaviour.

If you override Awake(), you need to call “base.Awake();” within that method so that it starts up the co-routine properly.

You should now be able to do something like this…

Thread t1 = new Thread(delegate()
{
	while(true)
	{
		Invoke(delegate() 
		{
			Debug.Log("Resetting position...");
			this.transform.position = new Vector3(0, 0, 0);
		});
		
		// Doing some stuff
		
		Thread.Sleep(5000);
	}
	
});
t1.Start();

Here’s the source for SafeMonoBehaviour.

using UnityEngine;
using System.Collections;
using System.Threading;

public class SafeMonoBehaviour : MonoBehaviour 
{
	public delegate void InvokeDelegate();
	
	private Mutex invokeMutex = new Mutex();
	
	private InvokeDelegate invokeDelegate = null;
	
	public void Invoke(InvokeDelegate d)
	{
		invokeMutex.WaitOne();
		
		invokeDelegate = d;

		invokeMutex.ReleaseMutex();
		
		while(true)
		{
			Thread.Sleep(10);

			invokeMutex.WaitOne();
			
			if(invokeDelegate != d)
			{
				invokeMutex.ReleaseMutex();
				return;
			}
			
			invokeMutex.ReleaseMutex();
		}
	}
	
	private IEnumerator InvokeOnce()
	{
		while(true)
		{
			invokeMutex.WaitOne();
			
			if(invokeDelegate != null)
			{
				invokeDelegate();
				invokeDelegate = null;
			}
				
			invokeMutex.ReleaseMutex();
			
			yield return null;
		}
	}
	
	public void Awake()
	{
		StartCoroutine(InvokeOnce());
	}
}

Maybe someone find this useful. =)

Oh, and Invoke(delegate) returns once the delegate has been executed in the main thread.

Alternative implementation.

Invoker.Invoke(delegate) is a static function in this variant.

To use it, just add “Invoker” as a script to any game object (such as the main camera).

You only have it add it to one game object.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

public class Invoker : MonoBehaviour 
{
	public delegate void InvokeDelegate();
	
	private static Thread mainThread = null;
	
	private class InvokeObject
	{
		public InvokeDelegate d;
		
		public bool invoked = false;
		
		public InvokeObject(InvokeDelegate d)
		{
			this.d = d;
		}
	}
	
	private static List<InvokeObject> invokeObjects = new List<InvokeObject>();
	
	public static void Invoke(InvokeDelegate d)
	{
		InvokeObject iObj = null; 
		
		lock(invokeObjects)
		{
			if(mainThread == null)
			{
				return;
			}
			
			if(mainThread == Thread.CurrentThread)
			{
				// It would seem we're already in the main thread. We'll just call the
				// delegate ourselves and then return.
				d();
				return;
			}
			
			iObj = new InvokeObject(d);
			invokeObjects.Add(iObj);
		}
		
		while(true)
		{
			Thread.Sleep(10);
			
			// Wait until the delegate has been removed from the list.
			lock(invokeObjects)
			{
				if(iObj.invoked)
				{
					return;
				}
			}
		}
	}	
	
	private static IEnumerator InvokeOnce()
	{
		while(true)
		{
			lock(invokeObjects)
			{
				if(mainThread == null)
				{
					// If this is the first time we reach this point; let's remember
					// this thread for comparison later.
					mainThread = Thread.CurrentThread;
				}
				
				if(invokeObjects.Count > 0)
				{
					invokeObjects[0].d();
					invokeObjects[0].invoked = true;
					invokeObjects.RemoveAt(0);
				}
			}
				
			yield return null;
		}
	}
	
	public void Awake()
	{
		StartCoroutine(InvokeOnce());
	}	
}

Edit: Fixed so that it queues up delegates.

Edit: Use lock() instead of Mutex.

Edit: Some fixes.

Wow, this seems very very useful indeed. :smile:

So, this makes it safe to call Unity API stuff from within another thread?
If that’s the case, that’s an amazing achievement there!!

Thanks for sharing it with the community!! :slight_smile:

Cheers

Yeah, that’s what it does.

You can choose to invoke an anonymous function, or something else.

It will be called from the main thread. :wink:

Invoker.Invoke(delegate() { Debug.Log(“Called from a good thread.”); });

Improved version.

As usual, the usage is Invoker.Invoke(delegate).

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

public class Invoker : MonoBehaviour 
{
	public delegate void InvokeDelegate();
	
	private static Thread mainThread = null;
		
	private static List<InvokeDelegate> invokeDelegates = new List<InvokeDelegate>();	
	
	public static void Invoke(InvokeDelegate d)
	{
		lock(invokeDelegates)
		{
			if(mainThread == null)
			{
				// We can't continue unless we know which thread is the main thread.
				return;
			}
			
			if(mainThread == Thread.CurrentThread)
			{
				// It would seem we're already in the main thread. We'll just call the
				// delegate ourselves and then return.
				d();
				return;
			}
			
			// Acquire a lock on the object.
			Monitor.Enter(d);
			
			// Add the object to the list.
			invokeDelegates.Add(d);
		}
		
		// Let's wait until InvokeOnce() is finished with the object.
		Monitor.Wait(d);
	}	
	
	private static IEnumerator InvokeOnce()
	{
		while(true)
		{
			lock(invokeDelegates)
			{
				foreach(InvokeDelegate d in invokeDelegates)
				{
					lock(d)
					{
						d();
						Monitor.Pulse(d);
					}
				}
				
				invokeDelegates.Clear();
			}
				
			yield return null;
		}
	}
	
	public void Awake()
	{
		mainThread = Thread.CurrentThread;
		StartCoroutine(InvokeOnce());
	}	
}

Gonna try to add it to the wiki later, but it keeps complaining about spam protection.