Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

To Async or not to async.... That is the question...

Discussion in 'General Discussion' started by Epictickle, Mar 30, 2022.

  1. Epictickle

    Epictickle

    Joined:
    Aug 12, 2012
    Posts:
    431
    Hi All,

    So I've been out of the Unity game for a hot minute, but I've recently gotten the itch, so I'm working on a small POC game with a buddy of mine.. It'll be multiplayer, and as such, requires authentication. I actually already have an OIDC compliant identity solution that I built myself (I'm quite proud of that, thanks), and would like to go ahead and use that. I went ahead and generated a client_id and client_secret for the game and needed a place to store, at least the secret, so I figured I might as well try out Remote Config.

    I've been using Unity for quite some time and from what I remember, the docs say (quite a few times), that it is not a thread-safe solution and if you use async/await, all joy and happiness in your life will cease to exist, unity technologies will kick your door down, and the feds may-or-may-not get involved.

    Sarcasm aside, though, I've heard it's a bad idea, so I've avoided it.

    Now, I was checking out the docs for RemoteConfig (preview), here: https://docs.unity3d.com/Packages/com.unity.remote-config@3.0/manual/CodeIntegration.html
    and to my surprise they are using async/await. Now thinking about it, I'm assuming async/await is only unsafe in context of the Update function, or has Unity become thread-safe over the ~3 years I was not using it? I tried googling but wasn't able to find much of an answer.

    Thanks!
     
  2. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,136
    Unity's APIs are not thread safe but that doesn't mean you can't use threads. You just have to have all of your Unity API calls on the main thread.

    One of the methods that I use is to wrap up the Unity API calls in an Action and then pass it to an instance of a ConcurrentQueue. The ConcurrentQueue is then checked every frame in an Update() where if it isn't empty the next action is pulled off of it and processed.

    I mostly use this with async tasks but it's also good for cases where you need to perform intensive calculations prior to calling Unity APIs. Procedural mesh generation, for example.

    Code (csharp):
    1. ConcurrentQueue<Action> actionQueue = new ConcurrentQueue<Action>();
    Code (csharp):
    1. private void Update()
    2. {
    3.     if (!actionQueue.IsEmpty)
    4.     {
    5.         while (actionQueue.TryDequeue(out var action))
    6.             action?.Invoke();
    7.     }
    8. }
    Code (csharp):
    1. actionQueue.Enqueue(() =>
    2. {
    3.     // Unity API calls go here
    4. });
     
    Last edited: Mar 31, 2022
    Epictickle likes this.
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,883
    If you're a fan of a bit of async code there is this plugin: https://github.com/Cysharp/UniTask which lets you do a lot more async stuff than you would by default. It hooks into the player loop, keeping your calls inline with the Unity API.

    Not to mention its UniTask struct is more performant than the regular Task class.
     
    NavidK0 and PanthenEye like this.
  4. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,323
    Long story short, if you try to asynchronously work with unity resources from another thread, expect things to break.

    However, if you offload some work that does not touch any unity resources into asynchronous function, and await them from within monobehavior functions that would be fine.

    The problem is not multithreading, but that unity objects were not made with multithreading in mind and are to be treated as "not thread safe".

    Also, do keep in mind that it is safer not to touch multithreading unless you have to.
     
  5. Epictickle

    Epictickle

    Joined:
    Aug 12, 2012
    Posts:
    431
    @Ryiah That's a very elegant way to handle it; I hadn't thought of using a Command pattern; that would open up a ton of possibilities actually. Thank you!!!

    @neginfinity Yeah, that makes sense. Being a web dev full-time I normally eat, sleep, and breathe async/await so I was just a bit excited that it might be a possibility. Definitely agree it's safer in the context of a video game, but man, it does cut down code by a good bit. Also, just my personal opinion, but async/await (due to the shorter code), is a bit easier on the eyes when trying to read.
     
  6. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,323
    Try coroutines. They are executed on the main thread.

    The big difference between web and gamedev is that in gamedev, everything you want to do (during current frame) have to be done in 16 milliseconds if you want 60 fps. The web has no such restrictions.
     
    DungDajHjep likes this.
  7. EternalAmbiguity

    EternalAmbiguity

    Joined:
    Dec 27, 2014
    Posts:
    3,144
    I think you're better off using Tasks personally.

    You can store the main thread context to a static variable/class when starting out and use that to jump back on if necessary.
     
  8. NavidK0

    NavidK0

    Joined:
    May 19, 2013
    Posts:
    9
    I second this, UniTask is fantastic and lets you use async/await without issues in Unity. We use it for turn-based logic as well as UI interactions in our game.
     
    PanthenEye likes this.
  9. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    1,763
    DungDajHjep likes this.