wake-up-neo.com

Verwenden Sie die Unity-API aus einem anderen Thread oder rufen Sie eine Funktion im Haupt-Thread auf

Mein Problem ist, ich versuche, Unity-Socket zu verwenden, um etwas zu implementieren. Jedes Mal, wenn ich eine neue Nachricht erhalte, muss ich sie auf den Updattext aktualisieren (es handelt sich um einen Unity-Text). Wenn ich den folgenden Code durchführe, ruft das Void-Update jedoch nicht jedes Mal auf. 

Der Grund dafür, dass ich updatetext.GetComponent<Text>().text = "From server: "+tempMesg; nicht in die void getInformation einbeziehe, ist diese Funktion im Thread. Wenn ich das in getInformation () einbeziehe, kommt es zu einem Fehler: 

getcomponentfastpath can only be called from the main thread

Ich denke, das Problem ist, dass ich nicht weiß, wie man den Haupt-Thread und den untergeordneten Thread in C # zusammen ausführt. Oder gibt es vielleicht andere Probleme ... Hoffe, jemand kann helfen ... Es gibt meinen 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);
        }
    }
}
14
user6142261

Unity ist nicht Thread sicher, daher haben sie beschlossen, es unmöglich zu machen, ihre API von einer anderen Thread aufzurufen, indem sie einen Mechanismus zum Auslösen einer Ausnahme hinzufügten, wenn ihre API von einer anderen Thread verwendet wird. ].

Diese Frage wurde so oft gestellt, aber es gab keine richtige Lösung/Antwort auf eine von ihnen. Die Antworten lauten normalerweise "benutze ein Plugin" oder mache etwas, das nicht threadsicher ist. Hoffentlich ist dies der letzte.

Die Lösung, die Sie normalerweise auf der Forum-Website von Stackoverflow oder Unity finden, besteht darin, einfach eine boolean -Variable zu verwenden, um dem Haupt-Thread mitzuteilen, dass Sie Code in der Haupt-Thread -Variable ausführen müssen. Dies ist nicht richtig, da es nicht thread-safe ist und Ihnen keine Kontrolle darüber gibt, welche Funktion aufgerufen werden soll. Was ist, wenn Sie mehrere Threads haben, die den Haupt-Thread benachrichtigen müssen?

Eine andere Lösung, die Sie sehen werden, ist die Verwendung einer Coroutine anstelle einer Thread. Dies funktioniert nicht . Die Verwendung von Coroutine für Sockel ändert nichts. Sie werden immer noch mit Ihren Einfrieren Problemen enden. Sie müssen sich an Ihren Thread - Code halten oder Async verwenden.

Zu diesem Zweck können Sie eine Sammlung wie List erstellen. Wenn Sie im Haupt-Thread etwas ausführen möchten, rufen Sie eine Funktion auf, die den auszuführenden Code in einem Action speichert. Kopieren Sie das List von Action in ein lokales List von Action und führen Sie den Code aus dem lokalen Action in diesem List löschen Sie dann das List. Dies verhindert, dass andere Threads warten müssen, bis die Ausführung abgeschlossen ist.

Sie müssen auch einen volatile boolean Hinzufügen, um die Funktion Update zu benachrichtigen, dass in dem auszuführenden List Code wartet. Wenn Sie die List in eine lokale List kopieren, sollte dies um das Schlüsselwort lock herum erfolgen, um zu verhindern, dass ein anderer Thread darauf schreibt.

Ein Skript, das das ausführt, was ich oben erwähnt habe:

UnityThread Skript:

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

[~ # ~] Verwendung [~ # ~] :

Mit dieser Implementierung können Sie Funktionen in den 3 am häufigsten verwendeten Unity-Funktionen aufrufen: Update, LateUpdate und FixedUpdate funktioniert. Auf diese Weise können Sie auch eine Coroutine-Funktion im Hauptfenster Thread aufrufen. Es kann erweitert werden, um Funktionen in anderen Unity-Rückruffunktionen wie OnPreRender und OnPostRender aufrufen zu können.

1 Initialisieren Sie es zunächst mit der Funktion Awake().

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

2 So führen Sie einen Code in der Hauptdatei Thread eines anderen Threads aus:

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

Dadurch wird das aktuelle Objekt, an das der Scipt angehängt ist, um 90 Grad gedreht. Sie können jetzt die Unity-API (transform.Rotate) In einer anderen Thread verwenden.

3 So rufen Sie eine Funktion im Haupt-Thread Thread von einem anderen Thread aus auf:

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


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

Die # 2 und # 3 Samples werden im Update Funktion.

4 So führen Sie einen Code in der Funktion LateUpdate eines anderen Threads aus:

Beispiel hierfür ist ein Kamera-Tracking-Code.

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

5 So führen Sie einen Code in der Funktion FixedUpdate eines anderen Threads aus:

Ein Beispiel dafür, wenn Sie physikalische Aufgaben ausführen, wie das Hinzufügen von Kraft zu Rigidbody.

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

6 So starten Sie eine Coroutine-Funktion im Haupt-Thread Thread von einem anderen Thread aus:

UnityThread.executeCoroutine(myCoroutine());

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

Wenn Sie in den Funktionen LateUpdate und FixedUpdate nichts ausführen müssen, sollten Sie beide Zeilen dieses Codes auskommentieren unten:

//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK
//#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK

Dies erhöht die Leistung.

50
Programmer

Vieles, was in Unity über Threads geschrieben wird, ist in der Tat völlig falsch.

Wie?

Die grundlegende Tatsache ist, dass Unity natürlich vollständig rahmenbasiert ist.

Wenn Sie in einem rahmenbasierten System arbeiten, unterscheiden sich Threading-Probleme erheblich von denen in einem herkömmlichen System.

Es ist ein völlig anderes Paradigma.

(Tatsächlich ist es in vielerlei Hinsicht viel einfacher.)

Threading-Probleme auf einem Frame-basierten System unterscheiden sich grundlegend von denen auf einem herkömmlichen System. (In mancher Hinsicht ist vieleinfacherzu handhaben, da bestimmte Konzepte in einem rahmenbasierten System einfach nicht existieren.)

Angenommen, Sie haben ein Unity-Thermometer-Display, das einen bestimmten Wert anzeigt

Thermo.cs

enter image description here

Es wird also eine Funktion haben, die zB in Update aufgerufen wird

func void ShowThermoValue(float fraction) {
   display code, animates a thermometer
}

Das läuft nur einmal pro Frame und das war's auch schon.

Es ist sehr leicht, diese grundlegende Tatsache zu vergessen .

Klar, es läuft nur auf dem "Hauptthread".

(Es gibt nichts anderes in Unity! Es gibt nur ............... "den Unity-Thread"!)

Das wichtigste Paradigma ist jedoch:es wird einmal pro Frame ausgeführt.

An einer anderen Stelle in Thermo.cs haben Sie jetzt eine Funktion, die das Konzept "Ein neuer Wert ist angekommen" behandelt:

[MonoPInvokeCallback(typeof(ipDel))]
public static void NewValueArrives(float f) {

    ... ???
}

Beachten Sie, dass natürlich eine Klassenfunktion ist!

Sie können in keiner Weise auf eine normale Unity-Funktion wie ShowThermoValue zugreifen !!!!!! Fußnote 1

Es ist ein völlig bedeutungsloses Konzept. Sie können ShowThermoValue nicht "erreichen".

Alle "Threading-bezogenen" Codebeispiele, die Sie für Unity sehen und die versuchen, "in" einen Thread zu "gelangen", sindvollständig und völlig fehlgeleitet.

Es sind keine Themen zu erreichen!

Wiederum - und das ist überraschend - sind viele der Artikel und Beiträge auf konzeptioneller Ebene völlig inkorrekt, wenn Sie nur das gesamte Schreiben über Threads in Unity im Internet googeln.

Sagen wir: Werte kommen sehr häufig und unregelmäßig an.

Sie könnten verschiedene wissenschaftliche Geräte an ein Rack mit PCs und iPhones anschließen, und neue "Temperatur" -Werte könnten sehr häufig eintreffen .... Dutzende Male pro Frame ... oder vielleicht nur alle paar Sekunden.

Also, was machst du mit den Werten?

Einfacher geht es nicht.

Alles, was Sie aus dem Thread mit den ankommenden Werten tun, ist, darauf zu warten, dass eine Variable in der Komponente festgelegt wird !!

WTF? Alles was Sie tun, isteine Variable setzen? Das ist es? Wie kann es so einfach sein?

Dies ist eine dieser ungewöhnlichen Situationen:

  1. Ein Großteil des Schreibens auf Threads in Unity ist einfach völlig falsch. Es ist nicht so, als hätte es "einen Fehler" oder "etwas, das korrigiert werden muss". Es ist einfach "völlig und völlig falsch": auf der Ebene des Grundparadigmas.

  2. Überraschenderweise ist der eigentliche Ansatz absurd einfach.

  3. Es ist so einfach, dass Sie vielleicht denken, dass Sie etwas falsch machen.

Also muss die Variable ...

[System.Nonserialized] public float latestValue;

Setze es aus dem "ankommenden Thread" ...

[MonoPInvokeCallback(typeof(ipDel))]
public static void NewValueArrives(float f) {

    ThisScript.runningInstance.latestValue = f; // done
}

Ehrlich gesagt ist es das.

Um der weltweit größte Experte für "Threading in Unity" zu sein - was offensichtlich auf Frames basiert -, gibt es nichts weiter zu tun als das oben Genannte.

Und jedes Mal, wenn ShowThermoValue aufgerufen wird, zeigen Sie einfach diesen Wert an!

Wirklich, das war's!

[System.Nonserialized] public float latestValue;
func void ShowThermoValue() { // note NO arguments here!
   display code, animates a thermometer
   thermo height = latestValue
}

Sie zeigen einfach den "neuesten" Wert an.

latestValue wurde möglicherweise einmal, zweimal, zehnmal oder hundertmal für diesen Frame festgelegt. Sie zeigen jedoch einfach an, welcher Wert auch immer ist, wenn ShowThermoValue diesen Frame ausführt !

Was könnten Sie sonst noch anzeigen?

Das Thermometer wird auf dem Bildschirm mit 60 fps aktualisiert. Fußnote 2

So einfach ist das eigentlich. So einfach ist das. Überraschend aber wahr.


(Kritisch - vergessen Sie nicht, dass vector3 usw. in Unity NICHT atomar sind/C #)

Wie der Benutzer @dymanoid hervorgehoben hat (lesen Sie die wichtige Diskussion weiter unten), ist es wichtig, sich daran zu erinnern, dass Float im Unity/C # -Milieu atomar ist, während alles andere (z. B. Vector3 usw.) IS NICHT ATOMISCH. Typischerweise (wie im Beispiel hier) leiten Sie Floats nur aus Berechnungen von beispielsweise nativen Plugins weiter. Aber es ist wichtig zu wissen, dass Vektoren und so weiter NICHT atomar sind.


Manchmal geraten erfahrene Threading-Programmierer in einen Knoten mit einem rahmenbasierten System, weil in einem rahmenbasierten System die meisten Probleme, die durch Probleme mit der Rennbahn und dem Sperren verursacht werden, konzeptionell nicht existieren.

In einem rahmenbasierten System sollten alle Spielelemente einfach angezeigt werden oder sich basierend auf einem "aktuellen Wert" verhalten, der irgendwo eingestellt ist. Wenn Sie Informationen von anderen Threads haben, setzen Sie diese Werte einfach auf-done.

Sie können nicht sinnvoll"mit dem Hauptthread " in Unity, da dieser Hauptthread ............. rahmenbasiert ist!

Abgesehen von Threading-Problemen kann ein kontinuierliches System nicht sinnvoll mit einem Frame-Paradigma-System sprechen.

Die meisten Sperr-, Blockierungs- und Rennstreckenprobleme sind nicht vorhanden im rahmenbasierten Paradigma, weil: wenn Sie latestValue festgelegt haben Zehnmal, eine Million Mal, eine Milliarde Mal in einem bestimmten Rahmen. Was können Sie tun? .. Sie können nur einen Wert anzeigen!

Denken Sie an eine altmodische Plastikfolie. Sie haben buchstäblich nur ... einen Rahmen, und das war's. Wenn Sie in einem bestimmten Frame eine Billion Mal den neuesten Wert eingestellt haben, zeigt ShowThermoValue einfach (für diese 60stel Sekunde) den einen Wert an, den es beim Ausführen erfasst.

Alles, was Sie tun, ist: Lassen Sie Informationen irgendwo, die das Frame-Paradigma-System verwenden wird, wenn es möchte .

Das ist alles auf den Punkt gebracht.

Daher verschwinden die meisten "Threading-Probleme"in Unity.

Alles, was Sie tun könnenvon

  • andere Berechnungsthreads oder

  • von Plugin-Threads,

ist nur "Drop-Off-Werte", die das Spiel verwenden kann.

Das ist es!


Fußnoten


1 Wie konntest du? Vergessen Sie als Gedankenexperiment das Problem, dass Sie sich in einem anderen Thread befinden. ShowThermoValue wird einmal pro Frame von der Frame-Engine ausgeführt. Sie können es nicht auf sinnvolle Weise "nennen". Anders als in normaler OO Software können Sie eine Instanz der Klasse (eine Komponente ohne Bedeutung) nicht instanziieren und diese Funktion ausführen - das ist völlig ohne Bedeutung.

Bei der "normalen" Thread-Programmierung können Threads zurück und vor und so weiter sprechen, und dabei haben Sie Bedenken hinsichtlich des Sperrens, der Rennstrecke und so weiter. Aber das ist alles bedeutungslos in einem ECS-Frame-basierten System. Es gibt nichts zu "reden".

Nehmen wir an, dass Unity tatsächlich Multithreaded war !!!! Die Unity-Leute haben also den ganzen Motor in Multithread-Manier laufen lassen.Es würde keinen Unterschied machen- Sie können ShowThermoValue nicht in irgendeiner sinnvollen Weise aufrufen! Es ist eine Komponente, die die Frame-Engineeinmal pro Frame ausführtund das war's.

Also NewValueArrivesist nirgendwo - es ist eine Klassenfunktion!

Beantworten wir die Frage in der Überschrift:

Msgstr "Unity API von einem anderen Thread verwenden oder eine Funktion im Hauptthread aufrufen?"

Das Konzept ist >> völlig bedeutungslos <<. Unity basiert (wie alle Game Engines) auf Frames. Es gibt kein Konzept, eine Funktion im Haupt-Thread "aufzurufen". Um eine Analogie zu ziehen: Es wäre wie bei einem Kameramann in der Zeit des Zelluloidfilms, der fragt, wie man etwas tatsächlichaufeinem der Bilder "bewegt".

enter image description here

Das ist natürlich sinnlos. Sie können lediglich etwas für das nächste Foto, das nächste Bild, ändern.


2 Ich beziehe mich auf den "Arrival-Values-Thread" ... in der Tat! NewValueArrives kann auf dem Hauptthread ausgeführt werden oder nicht !!!! Es kann auf dem Thread des Plugins oder auf einem anderen Thread laufen! Es kann tatsächlich zu dem Zeitpunkt, zu dem Sie den NewValueArrives-Aufruf bearbeiten, vollständig single-threaded sein!Es spielt einfach keine Rolle!In einem rahmenbasierten Paradigma können Sie nur die Informationen "herumliegen lassen", die Komponenten wie ShowThermoValue kann nach Belieben verwendet werden.

6
Fattie

Ich habe diese Lösung für dieses Problem verwendet. Erstellen Sie ein Skript mit diesem Code und hängen Sie es an ein Spielobjekt an:

using System;
using System.Collections.Generic;
using UnityEngine;

public class ExecuteOnMainThread : MonoBehaviour {

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

    void Update()
    {
        while (RunOnMainThread.Count > 0)
        {
            RunOnMainThread.Dequeue().Invoke();
        }
    }

}

Wenn Sie dann etwas im Haupt-Thread aufrufen und von einer anderen Funktion in Ihrer Anwendung auf die Unity-API zugreifen müssen:

ExecuteOnMainThread.RunOnMainThread.Enqueue(() => {

    // Code here will be called in the main thread...

});
1
Pelayo Méndez