Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Task.Yield() / Task.Delay(0) hang

Discussion in 'Experimental Scripting Previews' started by Dejavu2017, Feb 18, 2018.

  1. Dejavu2017

    Dejavu2017

    Joined:
    Sep 29, 2017
    Posts:
    14
    Code (CSharp):
    1. using System.Threading.Tasks;
    2.  
    3. using UnityEngine;
    4.  
    5. public class Test : MonoBehaviour
    6. {
    7.     void Start()
    8.     {
    9.         TestAsync();
    10.     }
    11.  
    12.     async void TestAsync()
    13.     {
    14.         int i = 0;
    15.         while (true)
    16.         {
    17.             Debug.Log($"### TestAsync: {++i}");
    18.             await Task.Delay(1); // ok
    19.             // await Task.Delay(0); // hang!
    20.             // await Task.Yield(); // hang!
    21.         }
    22.     }
    23. }
    24.  
    While posting this thread, I do notice that there is another thread talking about a similar issue: https://forum.unity.com/threads/help-with-async-await.470214/

    But regardless, the above code when attached to a scene will hang the whole Unity editor when using Task.Delay(0) and Task.Yield(), is this the expected behavior?
     
  2. mkderoy

    mkderoy

    Unity Technologies

    Joined:
    Oct 25, 2016
    Posts:
    22
    I've filed a bug for this, will update this post with the issuetracker asap
    https://issuetracker.unity3d.com/issues/task-dot-delay-0-hangs-with-async-await

    I've looked at this issue and found that Task.Delay(0) runs synchronously. You can find details about this behavior on stackoverflow https://stackoverflow.com/questions/36939006/not-using-await-task-delayx-x0-makes-ui-thread-freeze

    We have made one change due to this report however. Currently, when processing async/await continuations, we currently process the continuations that are spawned from existing continuations. This is why Task.Yield() would hang. Instead soon (once the changes land in 2018.1), if an "awaited" function calls await again, that continuation will be processed on to the next pass through the player loop. I should mention that we're also considering changes to where we process continuations in the playerloop (currently fixedupdate) since this behavior seems inconsistent in some ways with how other .NET implementations work.
     
    Last edited: Mar 7, 2018
  3. msfredb7

    msfredb7

    Joined:
    Nov 1, 2012
    Posts:
    166
    Did the process continuation move from Fixed Update ? I can't find any documentation on it. It'd be great to see that in the player loop here: https://docs.unity3d.com/Manual/ExecutionOrder.html
     
  4. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,343
    I stumbled across this and did some digging (Unity 2019 LTS). It seems it moved to Update (evaluated once per frame). It is called between Update() and LateUpdate() and after coroutine "yield null".

    Here is how the updated lifecycle graph would look like:

    LifeCycleWithAsync.png

    One caveat: they may be processed after the "Internal animation update", I have not tested that.
    Test was done with a loop like this:
    Code (CSharp):
    1. while (predicate() == false)
    2. {
    3.     await Task.Yield();
    4. }
    If you want to be extra sure it's done only once per frame you may use something like this:
    Code (CSharp):
    1.  
    2. // Thanks to https://github.com/modesttree/Unity3dAsyncAwaitUtil
    3. // License MIT: https://github.com/modesttree/Unity3dAsyncAwaitUtil/blob/master/License.md
    4. using System;
    5. using System.Threading;
    6. using System.Threading.Tasks;
    7. using UnityEngine;
    8.  
    9. public static class Awaiters
    10. {
    11.     readonly static WaitForUpdate _waitForUpdate = new WaitForUpdate();
    12.  
    13.     public static WaitForUpdate NextFrame
    14.     {
    15.         get { return _waitForUpdate; }
    16.     }
    17.  
    18.     public static async Task Until(CancellationToken ct, Func<bool> predicate)
    19.     {
    20.         while(predicate() == false && ct.IsCancellationRequested == false)
    21.         {
    22.             await NextFrame;
    23.         }
    24.     }
    25.  
    26.     public static async Task While(CancellationToken ct, Func<bool> predicate)
    27.     {
    28.         while (predicate() == true && ct.IsCancellationRequested == false)
    29.         {
    30.             await NextFrame;
    31.         }
    32.     }
    33. }
    Would be nice to get some confirmation for this.
     
    Last edited: May 25, 2021
  5. Threeyes

    Threeyes

    Joined:
    Jun 19, 2014
    Posts:
    80
    I am facing the new bug in Unity2021.3.0f1:
    When I use await Task.Yield() in Editor, everything works fine, but after I build the game, it just 'freeze' on this line of code. Here's the test code:
    Code (CSharp):
    1.     void Start()
    2.     {
    3.         Test();
    4.     }
    5.     async void Test()
    6.     {
    7.         Debug.LogError("Test Begin");
    8.         await Task.Yield();
    9.         Debug.LogError("Test End");//Will not be executed!
    10.     }
     
  6. MiTschMR

    MiTschMR

    Joined:
    Aug 28, 2018
    Posts:
    490
    Are you using IL2CPP? If yes, then it doesn’t work.
     
  7. Threeyes

    Threeyes

    Joined:
    Jun 19, 2014
    Posts:
    80
    Thanks for your reply, I am using the good old Mono.
     
  8. KAW0

    KAW0

    Joined:
    Jan 1, 2017
    Posts:
    15
    agritsishyn likes this.