gpt4 book ai didi

c# - 从另一个线程使用 Unity API 或在主线程中调用函数

转载 作者:可可西里 更新时间:2023-11-01 16:43:12 26 4
gpt4 key购买 nike

我的问题是我尝试使用 Unity 套接字来实现某些东西。每次收到新消息时,我都需要将其更新为 updattext(它是 Unity 文本)。但是,当我执行以下代码时,void update 不会每次都调用。

我不包括的原因updatetext.GetComponent<Text>().text = "From server: "+tempMesg;在 void getInformation 中,这个函数在线程中,当我将它包含在 getInformation() 中时,它会出现错误:

getcomponentfastpath can only be called from the main thread

我认为问题是我不知道如何在 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);
}
}
}

最佳答案

Unity 不是 Thread安全,所以他们决定让从另一个 Thread 调用他们的 API 变得不可能通过添加一种机制,当它的 API 被另一个人使用时抛出异常 Thread .

这个问题已经被问过很多次了,但是没有一个合适的解决方案/答案。答案通常是“使用插件”或做一些非线程安全的事情。希望这是最后一次。

您通常会在 Stackoverflow 或 Unity 的论坛网站上看到的解决方案是简单地使用 boolean变量让主线程知道你需要在主线程中执行代码 Thread .这是不对的,因为它不是线程安全的,并且无法让您控制提供要调用的函数。如果你有多个 Threads 怎么办?需要通知主线程吗?

您将看到的另一种解决方案是使用协程而不是 Thread .这不会工作。对套接字使用协程不会改变任何东西。你仍然会得到你的 freezing问题。你必须坚持你的 Thread代码或使用 Async .

执行此操作的正确方法之一是创建一个集合,例如 List .当您需要在主线程中执行某些操作时,调用一个存储要在 Action 中执行的代码的函数.复制 ListAction到本地ListAction然后从本地Action执行代码在那List然后清除List .这可以防止其他 Threads从不得不等待它完成执行。

您还需要添加一个 volatile boolean通知Update List中有代码等待的函数被执行。复制 List 时到本地List , 那应该包裹在 lock 周围关键字以防止另一个线程写入它。

执行我上面提到的内容的脚本:

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;
}
}
}

用法:

此实现允许您调用 3 最常用的 Unity 函数中的函数:Update , LateUpdateFixedUpdate功能。这还允许您在主程序中调用运行协程函数 Thread .它可以扩展为能够调用其他 Unity 回调函数中的函数,例如 OnPreRenderOnPostRender .

1。首先,从 Awake() 初始化它功能。

void Awake()
{
UnityThread.initUnityThread();
}

2.在主Thread中执行一段代码来自另一个线程:

UnityThread.executeInUpdate(() =>
{
transform.Rotate(new Vector3(0f, 90f, 0f));
});

这会将 scipt 附加到的当前对象旋转 90 度。您现在可以在另一个 transform.Rotate 中使用 Unity API( Thread) .

3.调用主函数Thread来自另一个线程:

Action rot = Rotate;
UnityThread.executeInUpdate(rot);


void Rotate()
{
transform.Rotate(new Vector3(0f, 90f, 0f));
}

#2#3 样本在 Update 中执行功能。

4.执行 LateUpdate 中的一段代码来自另一个线程的函数:

这方面的示例是相机跟踪代码。

UnityThread.executeInLateUpdate(()=>
{
//Your code camera moving code
});

5.执行 FixedUpdate 中的一段代码来自另一个线程的函数:

在进行物理操作时的示例,例如向 Rigidbody 添加力.

UnityThread.executeInFixedUpdate(()=>
{
//Your code physics code
});

6.在主程序中启动协程函数Thread来自另一个线程:

UnityThread.executeCoroutine(myCoroutine());

IEnumerator myCoroutine()
{
Debug.Log("Hello");
yield return new WaitForSeconds(2f);
Debug.Log("Test");
}

最后,如果您不需要在 LateUpdate 中执行任何操作和 FixedUpdate 函数,你应该在下面注释这段代码的两行:

//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
//#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK

这将提高性能。

关于c# - 从另一个线程使用 Unity API 或在主线程中调用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21626548/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com