How do I invoke functions on the main thread?

Is there anyway to invoke unity functions on the main thread? I’m building an Editor script that runs workers in background threads due to large processing loads so as not to freeze up the editor. I need to be able to call unity functions on the final result but this needs to run in the main thread. Is there a way to do this?

Hey guys,

I created a simple class for this reason that you can call with a one-liner.

You can use it like this:

public IEnumerator ThisWillBeExecutedOnTheMainThread() {
    Debug.Log ("This is executed from the main thread");
    yield return null;
}

public void ExampleMainThreadCall() {
    UnityMainThreadDispatcher.Instance().Enqueue(ThisWillBeExecutedOnTheMainThread());
}

Simply head over to GitHub - PimDeWitte/UnityMainThreadDispatcher: A simple, thread-safe way of executing actions (Such as UI manipulations) on the Unity Main Thread and start using it if you’d like.

You cannot really call into a running thread; instead, the thread you want to call (the main thread in your case) needs to maintain a queue of events. Other threads can push events into the queue and the owning thread will consume them from time to time. This is called the producer-consumer-pattern. An ‘event’ in this context is just a data structure that contains whatever information you need to send to the other thread.

That implies, of course, that the main thread will not receive the call immediately (synchronously) but with a delay. Well, that is the nature of threading. You cannot ‘interrupt’ a thread to make a call into it. If you need to pass data in both directions both threads will need a queue.

The queue will need to be thread-safe, of course. Simply lock it whenever you access it.

.NET has a thread dispatcher class that implements that pattern in a nice way and it even feels like making function calls. I am not sure you can use it in Unity, though, since every thread may only have one dispatcher and the Unity main thread may already have one. May be worth trying, though.

http://msdn.microsoft.com/de-de/library/system.windows.threading.dispatcher%28v=vs.85%29.aspx

If it does not work you will have to implement your own queue solution.

You can easily create a dispatcher to talk to Unity’s main loop by having a component read from a list of available actions. This Unity Gems Threading Article contains a package that can be used to achieve this.

It is used inconjunction with threads like this:

//Scale a mesh on a second thread
void ScaleMesh(Mesh mesh, float scale)
{
    //Get the vertices of a mesh
    var vertices = mesh.vertices;
    //Run the action on a new thread
    Loom.RunAsync(()=>{
        //Loop through the vertices
        for(var i = 0; i < vertices.Length; i++)
        {
            //Scale the vertex
            vertices <em>= vertices _* scale;_</em>

}
//Run some code on the main thread
//to update the mesh
Loom.QueueOnMainThread(()=>{
//Set the vertices
mesh.vertices = vertices;
//Recalculate the bounds
mesh.RecalculateBounds();
});

});
}
The download for the package is in the article. Basically using QueueOnMainThread passes the closure to be run on the main thread as soon as possible or after a specified delay.

Uncomplicated and efficient:

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

public class Dispatcher : MonoBehaviour
{
    public static void RunAsync(Action action) {
        ThreadPool.QueueUserWorkItem(o => action());
    }

    public static void RunAsync(Action<object> action, object state) {
        ThreadPool.QueueUserWorkItem(o => action(o), state);
    }

    public static void RunOnMainThread(Action action)
    {
        lock(_backlog) {
            _backlog.Add(action);
            _queued = true;
        }
    }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Initialize()
    {
        if(_instance == null) {
            _instance = new GameObject("Dispatcher").AddComponent<Dispatcher>();
            DontDestroyOnLoad(_instance.gameObject);
        }
    }

    private void Update()
    {
        if(_queued)
        {
            lock(_backlog) {
                var tmp = _actions;
                _actions = _backlog;
                _backlog = tmp;
                _queued = false;
            }

            foreach(var action in _actions)
                action();

            _actions.Clear();
        }
    }

    static Dispatcher _instance;
    static volatile bool _queued = false;
    static List<Action> _backlog = new List<Action>(8);
    static List<Action> _actions = new List<Action>(8);
}

If anyone is interested I reworked the Loom Class from @whydoidoit 's Unity Gems link to an EditorWindow. This way you can easily thread long processes without locking up the editor window.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.Threading;
using System;
using System.IO;
using System.Linq;

public class LoomEditorWindow : EditorWindow {
	
	public int maxThreads = 8;
	
	private int numThreads;
	private int _count;
	
	private bool m_HasLoaded = false;
	
	private List<Action> _actions = new List<Action>();
	private List<DelayedQueueItem> _delayed = new  List<DelayedQueueItem>();

	private List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
	private List<Action> _currentActions = new List<Action>();
	
	public struct DelayedQueueItem
	{
		public float time;
		public Action action;
	}
	
	protected void QueueOnMainThread(Action action)
	{
		QueueOnMainThread( action, 0f);
	}
	
	protected void QueueOnMainThread(Action action, float time)
	{
		if(time != 0)
		{
			lock(_delayed)
			{
				_delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action});
			}
		}
		else
		{
			lock (_actions)
			{
				_actions.Add(action);
			}
		}
	}
	
	protected Thread RunAsync(Action a)
	{
		while(numThreads >= maxThreads)
		{
			Thread.Sleep(1);
		}
		Interlocked.Increment(ref numThreads);
		ThreadPool.QueueUserWorkItem(RunAction, a);
		return null;
	}
	
	private void RunAction(object action)
	{
		try
		{
			((Action)action)();
		}
		catch
		{
		}
		finally
		{
			Interlocked.Decrement(ref numThreads);
		}
	}
	
	protected virtual void Start()
	{
		m_HasLoaded = true;
		
	}
	
	protected virtual void Update()
	{
		if(m_HasLoaded == false)
			Start();
		
		lock (_actions)
		{
			_currentActions.Clear();
			_currentActions.AddRange(_actions);
			_actions.Clear();
		}
		foreach(var a in _currentActions)
		{
			a();
		}
		lock(_delayed)
		{
			_currentDelayed.Clear();
			_currentDelayed.AddRange(_delayed.Where(d=>d.time <= Time.time));
			foreach(var item in _currentDelayed)
				_delayed.Remove(item);
		}
		foreach(var delayed in _currentDelayed)
		{
			delayed.action();
		}
	}
}

You simply need to save current SynchronizationContext in main thread in a variable and then you can call Post on it with your function in other threads, e.g.:
`

var mainThreadContext = System.Threading.SynchronizationContext.Current;
mainThreadContext.Post(_ => MainThreadFun(), null);

`

1 Like

For anyone still interested. You can also use GitHub - JohnBaracuda/com.baracuda.thread-dispatcher: Thread Dispatcher is an open source tool to pass the execution of a Delegate, Coroutine or Task from a background thread to the main thread, and await its completion or result on the calling thread as needed..

It’s a tool I made a while ago that also supports async/await, meaning that you can not only dispatch work to the main thread, but also await its result/completion on the background thread. There are already multiple overloads to dispatch Delegates, Task’s and Coroutines and many more utilities that can be useful when working with multiple threads.

here’s a solution that works without Coroutines and Loom. If it’s unimportant enough that it can wait 1 frame, just queue your data into a list for processing then use it like so:

  List<string> messageQueue = new List<string>(); 

   //your update function...
   private void Update()
   {
       while(messageQueue.Count > 0)
       {
           HandleMessage(messageQueue[0]);
           messageQueue.RemoveAt(0);
       }
   }
 void HandleMessage(string msg) {
    //do whatever...
 }

Simply add your data to messageQueue in whatever form (I used string) and it’ll be processed on the main thread next frame.