My problem is I try to use Unity socket to implement something. Each time, when I get a new message I need to update it to the updattext (it is a Unity Text). However, When I do the following code, the void update does not calling every time.
The reason for I do not include updatetext.GetComponent<Text>().text = "From server: "+tempMesg;
in the void getInformation is this function is in the thread, when I include that in getInformation() it will come with an error:
getcomponentfastpath can only be called from the main thread
I think the problem is I don't know how to run the main thread and the child thread in C# together? Or there maybe other problems.
Here is my code:
using UnityEngine; using System.Collections; using System; using System.Net.Sockets; using System.Text; using System.Threading; using UnityEngine.UI; public class Client : MonoBehaviour { System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient(); private Thread oThread; // for UI update public GameObject updatetext; String tempMesg = "Waiting..."; // Use this for initialization void Start () { updatetext.GetComponent<Text>().text = "Waiting..."; clientSocket.Connect("10.132.198.29", 8888); oThread = new Thread (new ThreadStart (getInformation)); oThread.Start (); Debug.Log ("Running the client"); } // Update is called once per frame void Update () { updatetext.GetComponent<Text>().text = "From server: "+tempMesg; Debug.Log (tempMesg); } void getInformation(){ while (true) { try { NetworkStream networkStream = clientSocket.GetStream (); byte[] bytesFrom = new byte[10025]; networkStream.Read (bytesFrom, 0, (int)bytesFrom.Length); string dataFromClient = System.Text.Encoding.ASCII.GetString (bytesFrom); dataFromClient = dataFromClient.Substring (0, dataFromClient.IndexOf ("$")); Debug.Log (" >> Data from Server - " + dataFromClient); tempMesg = dataFromClient; string serverResponse = "Last Message from Server" + dataFromClient; Byte[] sendBytes = Encoding.ASCII.GetBytes (serverResponse); networkStream.Write (sendBytes, 0, sendBytes.Length); networkStream.Flush (); Debug.Log (" >> " + serverResponse); } catch (Exception ex) { Debug.Log ("Exception error:" + ex.ToString ()); oThread.Abort (); oThread.Join (); } // Thread.Sleep (500); } } }
One thread runs at the start of a program by default. This is the “main thread”. The main thread creates new threads to handle tasks. These new threads run in parallel to one another, and usually synchronize their results with the main thread once completed.
Starting with Unity 2020.2, that first conversion step is multi-threaded, prior to Unity 2020.2, it is single threaded.
2 Replies. Debug. Log (as well as LogError, LogWarning, ...) are afaik the only methods that are actually threadsafe.
Unity is not Thread
safe, so they decided to make it impossible to call their API from another Thread
by adding a mechanism to throw an exception when its API is used from another Thread
.
This question has been asked so many times, but there have been no proper solution/answer to any of them. The answers are usually "use a plugin" or do something not thread-safe. Hopefully, this will be the last one.
The solution you will usually see on Stackoverflow or Unity's forum website is to simply use a boolean
variable to let the main thread know that you need to execute code in the main Thread
. This is not right as it is not thread-safe and does not give you control to provide which function to call. What if you have multiple Threads
that need to notify the main thread?
Another solution you will see is to use a coroutine instead of a Thread
. This does not work. Using coroutine for sockets will not change anything. You will still end up with your freezing problems. You must stick with your Thread
code or use Async
.
One of the proper ways to do this is to create a collection such as List
. When you need something to be executed in the main Thread, call a function that stores the code to execute in an Action
. Copy that List
of Action
to a local List
of Action
then execute the code from the local Action
in that List
then clear that List
. This prevents other Threads
from having to wait for it to finish executing.
You also need to add a volatile boolean
to notify the Update
function that there is code waiting in the List
to be executed. When copying the List
to a local List
, that should be wrapped around the lock
keyword to prevent another Thread from writing to it.
A script that performs what I mentioned above:
UnityThread
Script:
#define ENABLE_UPDATE_FUNCTION_CALLBACK #define ENABLE_LATEUPDATE_FUNCTION_CALLBACK #define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK using System; using System.Collections; using UnityEngine; using System.Collections.Generic; public class UnityThread : MonoBehaviour { //our (singleton) instance private static UnityThread instance = null; ////////////////////////////////////////////////UPDATE IMPL//////////////////////////////////////////////////////// //Holds actions received from another Thread. Will be coped to actionCopiedQueueUpdateFunc then executed from there private static List<System.Action> actionQueuesUpdateFunc = new List<Action>(); //holds Actions copied from actionQueuesUpdateFunc to be executed List<System.Action> actionCopiedQueueUpdateFunc = new List<System.Action>(); // Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame private volatile static bool noActionQueueToExecuteUpdateFunc = true; ////////////////////////////////////////////////LATEUPDATE IMPL//////////////////////////////////////////////////////// //Holds actions received from another Thread. Will be coped to actionCopiedQueueLateUpdateFunc then executed from there private static List<System.Action> actionQueuesLateUpdateFunc = new List<Action>(); //holds Actions copied from actionQueuesLateUpdateFunc to be executed List<System.Action> actionCopiedQueueLateUpdateFunc = new List<System.Action>(); // Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame private volatile static bool noActionQueueToExecuteLateUpdateFunc = true; ////////////////////////////////////////////////FIXEDUPDATE IMPL//////////////////////////////////////////////////////// //Holds actions received from another Thread. Will be coped to actionCopiedQueueFixedUpdateFunc then executed from there private static List<System.Action> actionQueuesFixedUpdateFunc = new List<Action>(); //holds Actions copied from actionQueuesFixedUpdateFunc to be executed List<System.Action> actionCopiedQueueFixedUpdateFunc = new List<System.Action>(); // Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame private volatile static bool noActionQueueToExecuteFixedUpdateFunc = true; //Used to initialize UnityThread. Call once before any function here public static void initUnityThread(bool visible = false) { if (instance != null) { return; } if (Application.isPlaying) { // add an invisible game object to the scene GameObject obj = new GameObject("MainThreadExecuter"); if (!visible) { obj.hideFlags = HideFlags.HideAndDontSave; } DontDestroyOnLoad(obj); instance = obj.AddComponent<UnityThread>(); } } public void Awake() { DontDestroyOnLoad(gameObject); } //////////////////////////////////////////////COROUTINE IMPL////////////////////////////////////////////////////// #if (ENABLE_UPDATE_FUNCTION_CALLBACK) public static void executeCoroutine(IEnumerator action) { if (instance != null) { executeInUpdate(() => instance.StartCoroutine(action)); } } ////////////////////////////////////////////UPDATE IMPL//////////////////////////////////////////////////// public static void executeInUpdate(System.Action action) { if (action == null) { throw new ArgumentNullException("action"); } lock (actionQueuesUpdateFunc) { actionQueuesUpdateFunc.Add(action); noActionQueueToExecuteUpdateFunc = false; } } public void Update() { if (noActionQueueToExecuteUpdateFunc) { return; } //Clear the old actions from the actionCopiedQueueUpdateFunc queue actionCopiedQueueUpdateFunc.Clear(); lock (actionQueuesUpdateFunc) { //Copy actionQueuesUpdateFunc to the actionCopiedQueueUpdateFunc variable actionCopiedQueueUpdateFunc.AddRange(actionQueuesUpdateFunc); //Now clear the actionQueuesUpdateFunc since we've done copying it actionQueuesUpdateFunc.Clear(); noActionQueueToExecuteUpdateFunc = true; } // Loop and execute the functions from the actionCopiedQueueUpdateFunc for (int i = 0; i < actionCopiedQueueUpdateFunc.Count; i++) { actionCopiedQueueUpdateFunc[i].Invoke(); } } #endif ////////////////////////////////////////////LATEUPDATE IMPL//////////////////////////////////////////////////// #if (ENABLE_LATEUPDATE_FUNCTION_CALLBACK) public static void executeInLateUpdate(System.Action action) { if (action == null) { throw new ArgumentNullException("action"); } lock (actionQueuesLateUpdateFunc) { actionQueuesLateUpdateFunc.Add(action); noActionQueueToExecuteLateUpdateFunc = false; } } public void LateUpdate() { if (noActionQueueToExecuteLateUpdateFunc) { return; } //Clear the old actions from the actionCopiedQueueLateUpdateFunc queue actionCopiedQueueLateUpdateFunc.Clear(); lock (actionQueuesLateUpdateFunc) { //Copy actionQueuesLateUpdateFunc to the actionCopiedQueueLateUpdateFunc variable actionCopiedQueueLateUpdateFunc.AddRange(actionQueuesLateUpdateFunc); //Now clear the actionQueuesLateUpdateFunc since we've done copying it actionQueuesLateUpdateFunc.Clear(); noActionQueueToExecuteLateUpdateFunc = true; } // Loop and execute the functions from the actionCopiedQueueLateUpdateFunc for (int i = 0; i < actionCopiedQueueLateUpdateFunc.Count; i++) { actionCopiedQueueLateUpdateFunc[i].Invoke(); } } #endif ////////////////////////////////////////////FIXEDUPDATE IMPL////////////////////////////////////////////////// #if (ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK) public static void executeInFixedUpdate(System.Action action) { if (action == null) { throw new ArgumentNullException("action"); } lock (actionQueuesFixedUpdateFunc) { actionQueuesFixedUpdateFunc.Add(action); noActionQueueToExecuteFixedUpdateFunc = false; } } public void FixedUpdate() { if (noActionQueueToExecuteFixedUpdateFunc) { return; } //Clear the old actions from the actionCopiedQueueFixedUpdateFunc queue actionCopiedQueueFixedUpdateFunc.Clear(); lock (actionQueuesFixedUpdateFunc) { //Copy actionQueuesFixedUpdateFunc to the actionCopiedQueueFixedUpdateFunc variable actionCopiedQueueFixedUpdateFunc.AddRange(actionQueuesFixedUpdateFunc); //Now clear the actionQueuesFixedUpdateFunc since we've done copying it actionQueuesFixedUpdateFunc.Clear(); noActionQueueToExecuteFixedUpdateFunc = true; } // Loop and execute the functions from the actionCopiedQueueFixedUpdateFunc for (int i = 0; i < actionCopiedQueueFixedUpdateFunc.Count; i++) { actionCopiedQueueFixedUpdateFunc[i].Invoke(); } } #endif public void OnDisable() { if (instance == this) { instance = null; } } }
USAGE:
This implementation allows you to call functions in the 3 most used Unity functions: Update
, LateUpdate
and FixedUpdate
functions. This also allows you call run a coroutine function in the main Thread
. It can be extended to be able to call functions in other Unity callback functions such as OnPreRender
and OnPostRender
.
1.First, initialize it from the Awake()
function.
void Awake() { UnityThread.initUnityThread(); }
2.To execute a code in the main Thread
from another Thread:
UnityThread.executeInUpdate(() => { transform.Rotate(new Vector3(0f, 90f, 0f)); });
This will rotate the current Object the scipt is attached to, to 90 deg. You can now use Unity API(transform.Rotate
) in another Thread
.
3.To call a function in the main Thread
from another Thread:
Action rot = Rotate; UnityThread.executeInUpdate(rot); void Rotate() { transform.Rotate(new Vector3(0f, 90f, 0f)); }
The #2 and #3 samples executes in the Update
function.
4.To execute a code in the LateUpdate
function from another Thread:
Example of this is a camera tracking code.
UnityThread.executeInLateUpdate(()=> { //Your code camera moving code });
5.To execute a code in the FixedUpdate
function from another Thread:
Example of this when doing physics stuff such as adding force to Rigidbody
.
UnityThread.executeInFixedUpdate(()=> { //Your code physics code });
6.To Start a coroutine function in the main Thread
from another Thread:
UnityThread.executeCoroutine(myCoroutine()); IEnumerator myCoroutine() { Debug.Log("Hello"); yield return new WaitForSeconds(2f); Debug.Log("Test"); }
Finally, if you don't need to execute anything in the LateUpdate
and FixedUpdate
functions, you should comment both lines of this code below:
//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK //#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK
This will increase performance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With