C# 从另一个线程使用Unity API或在主线程中调用函数
我的问题是我试图使用UnitySocket来实现一些东西。每次,当我收到一条新消息时,我都需要将其更新为updateText(它是统一文本)。但是,当我执行以下代码时,void更新不会每次都调用 我不包括C# 从另一个线程使用Unity API或在主线程中调用函数,c#,multithreading,unity3d,C#,Multithreading,Unity3d,我的问题是我试图使用UnitySocket来实现一些东西。每次,当我收到一条新消息时,我都需要将其更新为updateText(它是统一文本)。但是,当我执行以下代码时,void更新不会每次都调用 我不包括updatetext.GetComponent().text=“来自服务器:”+tempMesg是该函数在线程中,当我将该函数包含在getInformation()中时,它将出现错误: getcomponentfastpath只能从主线程调用 我想问题是我不知道如何在C#中同时运行主线程和子线程
updatetext.GetComponent().text=“来自服务器:”+tempMesg空getInformation中的code>是该函数在线程中,当我将该函数包含在getInformation()中时,它将出现错误:
getcomponentfastpath只能从主线程调用
我想问题是我不知道如何在C#中同时运行主线程和子线程?或者可能还有其他问题
这是我的密码:
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);
}
}
}
使用UnityEngine;
使用系统集合;
使用制度;
使用System.Net.Sockets;
使用系统文本;
使用系统线程;
使用UnityEngine.UI;
公共类客户端:MonoBehavior{
System.Net.Sockets.TcpClient clientSocket=新系统.Net.Sockets.TcpClient();
私有线程读取;
//用于用户界面更新
公共游戏对象更新文本;
字符串tempMesg=“等待…”;
//用于初始化
无效开始(){
updatetext.GetComponent().text=“等待…”;
clientSocket.Connect(“10.132.198.29”,8888);
oThread=新线程(newthreadstart(getInformation));
oThread.Start();
Log(“运行客户端”);
}
//每帧调用一次更新
无效更新(){
updatetext.GetComponent().text=“来自服务器:”+tempMesg;
Debug.Log(tempMesg);
}
void getInformation(){
while(true){
试一试{
NetworkStream NetworkStream=clientSocket.GetStream();
byte[]bytesFrom=新字节[10025];
networkStream.Read(bytesFrom,0,(int)bytesFrom.Length);
string dataFromClient=System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient=dataFromClient.Substring(0,dataFromClient.IndexOf(“$”);
Log(“>>来自服务器的数据-”+来自客户端的数据);
tempMesg=来自客户端的数据;
string serverResponse=“来自服务器的最后一条消息”+dataFromClient;
Byte[]sendBytes=Encoding.ASCII.GetBytes(serverResponse);
networkStream.Write(sendBytes,0,sendBytes.Length);
Flush();
Debug.Log(“>>”+serverResponse);
}捕获(例外情况除外){
Log(“异常错误:+ex.ToString());
oThread.Abort();
Join();
}
//睡眠(500);
}
}
}
Unity不是线程
安全的,因此他们决定通过添加一种机制,在从另一个线程使用其API时抛出异常,从而使从另一个线程
调用其API成为不可能
这个问题已经被问了很多次,但是没有一个正确的答案。答案通常是“使用插件”或做一些线程不安全的事情。希望这将是最后一次
您通常在Stackoverflow或Unity论坛网站上看到的解决方案是简单地使用布尔
变量,让主线程知道您需要在主线程中执行代码。这是不正确的,因为它不是线程安全的,并且不允许您控制要调用的函数。如果有多个线程需要通知主线程,该怎么办
您将看到的另一个解决方案是使用协程,而不是线程
。这不起作用。对套接字使用协同路由不会改变任何东西。你最终还是会遇到问题的。您必须坚持使用线程
代码或使用异步
正确的方法之一是创建一个集合,例如List
。当需要在主线程中执行某些操作时,请调用一个函数,该函数将要执行的代码存储在操作中。将操作的列表
复制到操作的本地列表
,然后从该列表中的本地操作
执行代码,然后清除该列表
。这可以防止其他线程
必须等待它完成执行
您还需要添加一个volatile boolean
,以通知Update
函数有代码在列表中等待执行。将列表
复制到本地列表
时,应将其环绕在锁
关键字周围,以防止其他线程向其写入
执行我上面提到的内容的脚本:
UnityThread
脚本:
#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;
}
}
}
2。要从另一个线程执行主线程中的代码,请执行以下操作:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
Action rot = Rotate;
UnityThread.executeInUpdate(rot);
void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}
UnityThread.executeCoroutine(myCoroutine());
IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}
这将将scipt连接到的当前对象旋转90度。现在可以在另一个线程中使用Unity API(transform.rotate
)
3。要从另一个线程调用主线程中的函数,请执行以下操作:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
Action rot = Rotate;
UnityThread.executeInUpdate(rot);
void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}
UnityThread.executeCoroutine(myCoroutine());
IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}
函数中执行#2和#3样本
void Awake()
{
UnityThread.initUnityThread();
}
4。要从另一个线程执行函数中的代码:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
Action rot = Rotate;
UnityThread.executeInUpdate(rot);
void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}
UnityThread.executeCoroutine(myCoroutine());
IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}
这方面的示例是摄影机跟踪代码
UnityThread.executeInLateUpdate(()=>
{
//Your code camera moving code
});
5。要从另一个线程执行函数中的代码:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
Action rot = Rotate;
UnityThread.executeInUpdate(rot);
void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}
UnityThread.executeCoroutine(myCoroutine());
IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}
例如,在做物理方面的事情时,比如给刚体
加力
UnityThread.executeInFixedUpdate(()=>
{
//Your code physics code
});
6。要从另一个线程在主线程中启动协同例程函数,请执行以下操作:
UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});
Action rot = Rotate;
UnityThread.executeInUpdate(rot);
void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}
UnityThread.executeCoroutine(myCoroutine());
IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}
最后,如果不需要在和函数中执行任何操作,则应在下面注释这段代码的两行:
//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
//#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK
这将提高性能