Here’s my class for calling functions to execute in main thread, with a simple example for usege.
I’ve used it in my game and it works perfect. And it’s much cleaner than the previous solution that I used.
/* Attach this to any object in your scene, to make it work */
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MainThread : MonoBehaviour {
class CallInfo
{
public Function func;
public object parameter;
public CallInfo(Function Func, object Parameter)
{
func = Func;
parameter = Parameter;
}
public void Execute()
{
func(parameter);
}
}
public delegate void Function(object parameter);
public delegate void Func();
static List<CallInfo> calls = new List<CallInfo>();
static List<Func> functions = new List<Func>();
static Object callsLock = new Object();
static Object functionsLock = new Object();
void Start()
{
calls = new List<CallInfo>();
functions = new List<Func>();
StartCoroutine(Executer());
}
public static void Call(Function Func, object Parameter)
{
lock(callsLock)
{
calls.Add(new CallInfo(Func, Parameter));
}
}
public static void Call(Func func)
{
lock(functionsLock)
{
functions.Add(func);
}
}
IEnumerator Executer()
{
while(true)
{
yield return new WaitForSeconds(0.01f);
while(calls.Count > 0)
{
calls[0].Execute();
lock(callsLock)
{
calls.RemoveAt(0);
}
}
while(functions.Count > 0)
{
functions[0]();
lock(functionsLock)
{
functions.RemoveAt(0);
}
}
}
}
}
How to call it:
MainThread.Call(YourFunction);
MainThread.Call(YourFunction, parameters);
Note that the function parameter must be “object”.
You’re going to want to be careful about accessing that collection like that.
For one, because ‘Call’ can be called from any thread… it could be called at the same time in 2 different threads. This would have colliding ‘Adds’.
Another thing is that ‘Call’ could be called while you’re in the midst of executing all the calls and removing them from the collections. Again causing collisions. List.Add and List.RemoveAt do not operate in a thread safe manner.
Furthermore, delegates have grouping built in, you don’t really need those Lists.
I actually shared in your other thread a version I use in my framework that operates in a thread safe manner.
http://forum.unity3d.com/threads/multi-threaded-usage-of-unity-api.348072/
@lordofduct You’re right, there is a small chance that those function may be called at the same time, which is why I have posted an edit with a lock implementation.
I’ve already checked out the code you posted, but I’ve decided not to use it, mostly because I didn’t see what is the principle behind it. Would you care to explain?
What do you mean the principle behind it?
I explained how it works in that post.
As well as commented the code in the link.
As well as showed a place in my code that I use it.
Here I’ll even show it to you wrapped up in the interface of the MonoBehaviour you created:
/* Attach this to any object in your scene, to make it work */
using UnityEngine;
using System.Collections;
public class MainThread : MonoBehaviour
{
private static InvokePump _pump;
void Start()
{
_pump = new InvokePump(); //it was created in the main thread, so the owner thread is main thread
StartCoroutine(Executer());
}
public static void Call(System.Action<object> func, object obj)
{
_pump.BeginInvoke(() => { func(obj); });
}
public static void Call(System.Action func)
{
_pump.BeginInvoke(func);
}
IEnumerator Executer()
{
//not sure why use use a coroutine and a tiny waitforseconds, but whatevs, I'll roll with it
var wait = new WaitForSeconds(0.01f);
while(true)
{
_pump.Update();
yield wait;
}
}
}
Although I probably wouldn’t use a coroutine, and I’d put some protection to make sure our singleton didn’t have multiple instances:
/* Attach this to any object in your scene, to make it work */
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MainThread : MonoBehaviour
{
#region Fields
private static InvokePump _pump;
private static MainThread _singleton;
#endregion
#region CONSTRUCTOR
void Awake()
{
if(_singleton != null)
{
Debug.LogWarning("Multiple MainThread InvokePumps were added to the scene.");
Object.Destroy(this);
return;
}
_pump = new InvokePump(); //it was created in the main thread, so the owner thread is main thread
_singleton = this;
}
#endregion
public static InvokePump Pump { get { return _pump; } }
public static void Call(System.Action<object> func, object obj)
{
_pump.BeginInvoke(() => { func(obj); });
}
public static void Call(System.Action func)
{
_pump.BeginInvoke(func);
}
private void Update()
{
_pump.Update();
}
}