From a2f631c52ec93375aeaf898f43438ed5c45f70ee Mon Sep 17 00:00:00 2001 From: Nicolas Vandamme Date: Mon, 26 Apr 2021 22:05:49 +0200 Subject: [PATCH 1/2] Use SemaphoreSlim insted of lock() --- UnityMainThreadDispatcher.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UnityMainThreadDispatcher.cs b/UnityMainThreadDispatcher.cs index 7a4f783..e76ec62 100644 --- a/UnityMainThreadDispatcher.cs +++ b/UnityMainThreadDispatcher.cs @@ -18,6 +18,7 @@ limitations under the License. using System.Collections; using System.Collections.Generic; using System; +using System.Threading; using System.Threading.Tasks; /// Author: Pim de Witte (pimdewitte.com) and contributors, https://github.com/PimDeWitte/UnityMainThreadDispatcher @@ -28,6 +29,7 @@ limitations under the License. public class UnityMainThreadDispatcher : MonoBehaviour { private static readonly Queue _executionQueue = new Queue(); + private static SemaphoreSlim _executionQueueLock = new SemaphoreSlim(1, 1); public void Update() { lock(_executionQueue) { @@ -42,11 +44,15 @@ public void Update() { /// /// IEnumerator function that will be executed from the main thread. public void Enqueue(IEnumerator action) { - lock (_executionQueue) { + _executionQueueLock.Wait(); + try { _executionQueue.Enqueue (() => { StartCoroutine (action); }); } + finally { + _executionQueueLock.Release(); + } } /// From f7830d858e821a5753bc55e0f8ade5a442206c5a Mon Sep 17 00:00:00 2001 From: Nicolas Vandamme Date: Mon, 26 Apr 2021 22:07:39 +0200 Subject: [PATCH 2/2] Use SemaphoreSlim instead of lock() --- UnityMainThreadDispatcher.cs | 209 +++++++++++++++++++---------------- 1 file changed, 115 insertions(+), 94 deletions(-) diff --git a/UnityMainThreadDispatcher.cs b/UnityMainThreadDispatcher.cs index e76ec62..331a2cd 100644 --- a/UnityMainThreadDispatcher.cs +++ b/UnityMainThreadDispatcher.cs @@ -26,100 +26,121 @@ limitations under the License. /// A thread-safe class which holds a queue with actions to execute on the next Update() method. It can be used to make calls to the main thread for /// things such as UI Manipulation in Unity. It was developed for use in combination with the Firebase Unity plugin, which uses separate threads for event handling /// -public class UnityMainThreadDispatcher : MonoBehaviour { - - private static readonly Queue _executionQueue = new Queue(); - private static SemaphoreSlim _executionQueueLock = new SemaphoreSlim(1, 1); - - public void Update() { - lock(_executionQueue) { - while (_executionQueue.Count > 0) { - _executionQueue.Dequeue().Invoke(); - } - } - } - - /// - /// Locks the queue and adds the IEnumerator to the queue - /// - /// IEnumerator function that will be executed from the main thread. - public void Enqueue(IEnumerator action) { - _executionQueueLock.Wait(); - try { - _executionQueue.Enqueue (() => { - StartCoroutine (action); - }); - } - finally { - _executionQueueLock.Release(); - } - } - - /// - /// Locks the queue and adds the Action to the queue - /// - /// function that will be executed from the main thread. - public void Enqueue(Action action) - { - Enqueue(ActionWrapper(action)); - } - - /// - /// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes - /// - /// function that will be executed from the main thread. - /// A Task that can be awaited until the action completes - public Task EnqueueAsync(Action action) - { - var tcs = new TaskCompletionSource(); - - void WrappedAction() { - try - { - action(); - tcs.TrySetResult(true); - } catch (Exception ex) - { - tcs.TrySetException(ex); - } - } - - Enqueue(ActionWrapper(WrappedAction)); - return tcs.Task; - } - - - IEnumerator ActionWrapper(Action a) - { - a(); - yield return null; - } - - - private static UnityMainThreadDispatcher _instance = null; - - public static bool Exists() { - return _instance != null; - } - - public static UnityMainThreadDispatcher Instance() { - if (!Exists ()) { - throw new Exception ("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene."); - } - return _instance; - } - - - void Awake() { - if (_instance == null) { - _instance = this; - DontDestroyOnLoad(this.gameObject); - } - } - - void OnDestroy() { - _instance = null; - } +public class UnityMainThreadDispatcher : MonoBehaviour +{ + + private static readonly Queue _executionQueue = new Queue(); + private static SemaphoreSlim _executionQueueLock = new SemaphoreSlim(1, 1); + + public void Update() + { + _executionQueueLock.Wait(); + try + { + while (_executionQueue.Count > 0) + { + _executionQueue.Dequeue().Invoke(); + } + } + finally + { + _executionQueueLock.Release(); + } + } + + /// + /// Locks the queue and adds the IEnumerator to the queue + /// + /// IEnumerator function that will be executed from the main thread. + public void Enqueue(IEnumerator action) + { + _executionQueueLock.Wait(); + try + { + _executionQueue.Enqueue(() => + { + StartCoroutine(action); + }); + } + finally + { + _executionQueueLock.Release(); + } + } + + /// + /// Locks the queue and adds the Action to the queue + /// + /// function that will be executed from the main thread. + public void Enqueue(Action action) + { + Enqueue(ActionWrapper(action)); + } + + /// + /// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes + /// + /// function that will be executed from the main thread. + /// A Task that can be awaited until the action completes + public Task EnqueueAsync(Action action) + { + var tcs = new TaskCompletionSource(); + + void WrappedAction() + { + try + { + action(); + tcs.TrySetResult(true); + } + catch (Exception ex) + { + tcs.TrySetException(ex); + } + } + + Enqueue(ActionWrapper(WrappedAction)); + return tcs.Task; + } + + + IEnumerator ActionWrapper(Action a) + { + a(); + yield return null; + } + + + private static UnityMainThreadDispatcher _instance = null; + + public static bool Exists() + { + return _instance != null; + } + + public static UnityMainThreadDispatcher Instance() + { + if (!Exists()) + { + throw new Exception("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene."); + } + return _instance; + } + + + void Awake() + { + if (_instance == null) + { + _instance = this; + DontDestroyOnLoad(this.gameObject); + } + } + + void OnDestroy() + { + _instance = null; + } }