UnityException: SetActive can only be called from the main thread

Hello,
I’m using a script that I’ve found on the web to listen to a port for messages.
When I receive a message, for example “ON”, I should turn on a Gameobject.
This script is working well, and I can receive the messages, but it create a new thread, and it seem that I can call SetActive only in the main thread.
I’ve tried to call another method in the main thread, when I receive the message in the new thread, but I always get this error “UnityException: SetActive can only be called from the main thread”.

How would it possible to call a method (with SetActive()) in the main thread, from a separate thread?
Or should I use another workaround?
Many thanks!

This is the script I’m using to listen to the port

/*
    -----------------------
    UDP-Receive (send to)
    -----------------------
    // [url]http://msdn.microsoft.com/de-de/library/bb979228.aspx#ID0E3BAC[/url]
  
  
    // > receive
    // 127.0.0.1 : 8051
  
    // send
    // nc -u 127.0.0.1 8051
*/
using UnityEngine;
using System.Collections;

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

public class UDPReceive : MonoBehaviour
{

    // receiving Thread
    Thread receiveThread;

    // udpclient object
    UdpClient client;

    // public
    // public string IP = "127.0.0.1"; default local
    public int port = 8051; // define > init

    // infos
    public string lastReceivedUDPPacket = "";
    public string allReceivedUDPPackets = ""; // clean up this from time to time!


    // start from shell
    private static void Main()
    {
        UDPReceive receiveObj = new UDPReceive();
        receiveObj.init();

        string text = "";
        do
        {
            text = Console.ReadLine();
        }
        while (!text.Equals("exit"));
    }
    // start from unity3d
    public void Start()
    {

        init();
    }

    // OnGUI
    void OnGUI()
    {
        Rect rectObj = new Rect(40, 10, 200, 400);
        GUIStyle style = new GUIStyle();
        style.alignment = TextAnchor.UpperLeft;
        GUI.Box(rectObj, "# UDPReceive\n127.0.0.1 " + port + " #\n"
                    + "shell> nc -u 127.0.0.1 : " + port + " \n"
                    + "\nLast Packet: \n" + lastReceivedUDPPacket
                    + "\n\nAll Messages: \n" + allReceivedUDPPackets
                , style);
    }

    // init
    private void init()
    {
        // Endpunkt definieren, von dem die Nachrichten gesendet werden.
        print("UDPSend.init()");

        // define port
        // port = 8051;

        // status
        print("Sending to 127.0.0.1 : " + port);
        print("Test-Sending to this Port: nc -u 127.0.0.1  " + port + "");


        // ----------------------------
        // Abhören
        // ----------------------------
        // Lokalen Endpunkt definieren (wo Nachrichten empfangen werden).
        // Einen neuen Thread für den Empfang eingehender Nachrichten erstellen.
        receiveThread = new Thread(
            new ThreadStart(ReceiveData));
        receiveThread.IsBackground = true;
        receiveThread.Start();

    }

    // receive thread
    private void ReceiveData()
    {

        client = new UdpClient(port);
        while (true)
        {

            try
            {
                // Bytes empfangen.
                IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = client.Receive(ref anyIP);

                // Bytes mit der UTF8-Kodierung in das Textformat kodieren.
                string text = Encoding.UTF8.GetString(data);

                // Den abgerufenen Text anzeigen.
                print(">> " + text);

                // latest UDPpacket
                lastReceivedUDPPacket = text;

                // ....
                allReceivedUDPPackets = allReceivedUDPPackets + text;

            }
            catch (Exception err)
            {
                print(err.ToString());
            }
        }
    }


    void OnApplicationQuit()
    {

        if (receiveThread.IsAlive)
        {
            receiveThread.Abort();
        }
        // receiver.Close();
    }

    // getLatestUDPPacket
    // cleans up the rest
    public string getLatestUDPPacket()
    {
        allReceivedUDPPackets = "";
        return lastReceivedUDPPacket;
    }
}

Well there’s your problem, and the error precisely states it.

Delegate queue to marshal tasks from other threads to the main thread:

https://discussions.unity.com/t/850153/2

You are not supposed to change the internal state of Unity objects from outside the main thread.
The most obvious solution is to change your own volatile state, from which the main thread will update the internal states once you know the threads have finished their jobs.

edit:
Kurt’s marshalling above is also good if you really want to push your imperative style. It is sufficient for what you need to achieve right now.

But in the general case, it’s not so good if your threads will fight each other over who will come first as this can introduce contradicting behavior. Accumulation is always a better option when you want to allow multi-threaded manipulation of a core state, but with accumulation you have to wait for the threads to settle down. If this is not desirable because of timing issues, and you want first-come-first-serve you then need to introduce a lock.

I think an implementation to what Kurt-Dekker hinted on could be found here: c# - Use Unity API from another Thread or call a function in the main Thread - Stack Overflow

2 Likes

Many thanks to all!
I’ll read carefully all your links

P.S. Maybe a dirty but super easy solution would be to check in Update if the message string is changed, and call SetActive there…

private void Update()
    {
        if (lastReceivedUDPPacket != "")
        {
            lastReceivedUDPPacket = "";
            go.SetActive(false);
        }
    }
2 Likes

I’m on the side of Fatty (on that thread) on this. But I do not find Kurt’s solution abhorrent, so there’s that.

It’s not dirty, that’s super recommended. You have changed some intermediate state which is a reflection of your threads’ intent. Once you are sure this is final, you update a deeper state with this intermediate information.

edit:
if you read geo_'s link thoroughly, check out the 2nd answer
in my opinion, it’s more truthful to how you actually circumvent the single-threadedness of Unity.
the original answer is too imperative, too pushy, fools you into thinking it’s synchronous, and may introduce a plethora of unwanted behavior, but most likely a race condition hell if you push it too far.

Now obviously you want to make that intermediate state accessible, readable, and optimal for your purpose.
There is no need for the code itself to look dirty.

Reading from this post:

“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”

What is interesting is that neither System events seem to work…

public class UDPReceiver{

     public static event Action() onMessageReceived:

     void MyNewThread(){
     ....
      onMessageReceived?.invoke();
     }
}


public class A : Monobehaviour {

Awake(){
  UDPReceiver.onMessageReceived += doSomething;
}

  void doSomething(){
  go.SetActive(false); /// It throw the error!

  }
}

you obviously cannot do a silly bypass. you’re still calling it from another thread.
observe that Kurt’s solution builds up a Queue of lambdas anonymous delegates, that are evaluated on the main thread after the fact.

Totally right, indeed