为了账号安全,请及时绑定邮箱和手机立即绑定

使用来自其他线程的统一API或调用主线程中的函数

/ 猿问

使用来自其他线程的统一API或调用主线程中的函数

泛舟湖上清波郎朗 2019-06-10 16:43:01

使用来自其他线程的统一API或调用主线程中的函数

我的问题是,我试图使用UnitySocket来实现一些东西。每次,当我收到一条新消息时,我需要将它更新为更新文本(它是一个统一文本)。但是,当我执行以下代码时,无效更新并不是每次都调用。

我不包括updatetext.GetComponent<Text>().text = "From server: "+tempMesg;在voidgetInformation中,这个函数在线程中,当我在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;


查看完整描述

3 回答

?
若吾皇

团结不是Thread安全,所以他们决定不可能从另一个调用他们的api。Thread通过添加一种机制,在从另一个地方使用其api时抛出异常。Thread.

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

您通常会在Stackoverover或United的论坛网站上看到的解决方案是,只需使用boolean变量使主线程知道您需要在主线程中执行代码。Thread..这是不对的,因为它是不对的。螺纹安全并且不给您提供调用哪个函数的控制权。如果你有多个Threads需要通知主线程吗?

您将看到的另一个解决方案是使用协同线而不是Thread..这确实是工作。对套接字使用协同线不会改变任何事情。你最终还是会冻结问题。你必须坚持你的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最常用的统一功能:UpdateLateUpdateFixedUpdate职能。这还允许您在主程序中调用运行cooutine函数。Thread..它可以扩展为能够在其他统一回调函数中调用函数,例如OnPreRenderOnPostRender.

1.首先,从Awake()功能。

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

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

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

这将旋转当前对象的枕木连接到90度。现在可以使用UnityAPI(transform.Rotate)在另一个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.在主目录中启动cooutine函数Thread来自另一个线程:

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

最后,如果不需要在LateUpdateFixedUpdate函数,您应该在下面的代码中对这两行代码进行注释:

//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK//#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK

这将提高性能。


查看完整回答
反对 2019-06-10
?
慕慕森

我一直在用这个解决方案来解决这个问题。使用此代码创建脚本,并将其附加到“游戏”对象:

using System;using System.Collections.Generic;using System.Collections.Concurrent;using UnityEngine;public class ExecuteOnMainThread :
 MonoBehaviour {

    public readonly static ConcurrentQueue<Action> RunOnMainThread = new ConcurrentQueue<Action>();

    void Update()
    {
        if(!RunOnMainThread.IsEmpty())
        {
           while(RunOnMainThread.TryDequeue(out action))
           {
             action.Invoke();
           }
        }
    }}

然后,当您需要调用主线程上的某个内容并从应用程序中的任何其他函数访问UnityAPI时:

ExecuteOnMainThread.RunOnMainThread.Enqueue(() => {

    // Code here will be called in the main thread...});


查看完整回答
反对 2019-06-10

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信