简体   繁体   中英

What Unity API's are not allowed in async callbacks?

I saw somewhere that within a Thread in Unity, you couldn't use Unity API's.

I'm wondering if this is also the case for async callbacks in general (for example a function assigned to WebSocket.OnMessage when using WebSocketSharp), and if so then is there a way to know what is allowed and what isn't (ie what are "Unity API's")?


As an example, when using WebSocketSharp's WebSocket.OnMessage , I put this in my Start function of a MonoBehavior :

// ws is a WebSocketSharp.WebSocket
// displayText is a UnityEngine.UI.Text

ws.OnMessage += (sender, evt) =>
  {
    // 1
    boo = UnityEngine.Random.Range(1, 1000).ToString();
    // 2
    displayText.text = "Heyoo";
  };

The line under 1 errors (no logs just beyond it) but no error message shows. Whereas when that line is not inside this callback (top-level of Update for example), I can see its result no problem.

As for the line under 2 , the Inspector in Unity shows the updated text, but the Play screen does not, until I update an attribute in the Inspector, as if the text field did get updated, but when it needed to use a Unity API to update the screen, it failed, so it's not until a separate update happens that it actually appears.

That's my hypothesis for these odd behaviors, so please let me know if that is correct, and if there's a succinct (or documented) way to describe what I'm describing.

async in general means exactly this: Not in the main thread.

It is hard to answer what is supported and what not in other threads then the mainthread ... short: Most things are not supported.

The UnityEngine.Random.Range(1, 1000).ToString(); should work. But be carefull with assignments!

A known workaround is to create like a callback worker and pass Action s to execute back to the main thread like eg:

public class MainThreadWorker : MonoBehaviour
{
    // singleton pattern
    public static MainThreadWorker Instance;

    // added actions will be executed in the main thread
    ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();

    private void Awake()
    {
        if (Instance)
        {
            this.enabled = false;
            return;
        }
        Instance = this;
    }

    private void Update()
    {
        // execute all actions added since the last frame
        while (actions.TryDequeue(out var action))
        {
            action?.Invoke();
        }
    }

    public void AddAction(Action action)
    {
        if(action != null) actions.Enqueue(action);
    }
}

Having this in your scene somewhere you can now pass an action back to the main thread like

ws.OnMessage += (sender, evt) =>
    {
        MainThreadWorker.Instance.AddAction(()=>
            {
                // 1
                boo = UnityEngine.Random.Range(1, 1000).ToString();
                // 2
                displayText.text = "Heyoo";      
            });
    };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM