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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Coroutine not running async?!

Discussion in 'Scripting' started by mondeon, Jan 12, 2017.

  1. mondeon

    mondeon

    Joined:
    May 29, 2015
    Posts:
    46
    Hello Unity Community,

    to understand Coroutine usage, I set up an example script and attached to the MainCam. It's content:
    Code (CSharp):
    1.  
    2. void Start () {
    3.   StartCoroutine(BigCounter());
    4. }
    5.  
    6.   private IEnumerator BigCounter()
    7.   {
    8.   for (int i = 0; i < 10000; i++)
    9.   {
    10.   Debug.Log("Hello" + i);
    11.   }
    12.   yield return null;
    13.   }
    When I start the game, it is completely blocked by the script and continues execution after coroutine ended. The debug output is shown just after coroutine ended, not generated in the output console window row by row. It doesn't matter if I yield before the for loop, result is the same... Blocking input and rendering completely. Now, you might say I should put the yield statement inside the for loop and I guess that might work, but imagine that the for-loop is some atomic operation that is heavy and cannot be torn apart (so I can't "inject" a yield inside).

    I thought with Coroutines I can start asynchronously another function, that does not block the main thread?? For example when I instantiate dynamically complex object structure, set dynamically it's sprite, add dynamically scripts to it and initialize them with desired values.. And to spawn that object without blocking the flaw of the game and it's execution.

    What am I doing wrong? Most important - how can I execute in Unity that for loop without blocking the normal game execution/input/rendering, to start a parallel/async process, so to say?

    Thanks for any response!

     
  2. Joboto

    Joboto

    Joined:
    Sep 12, 2013
    Posts:
    64
    Hi there,

    I don't believe that Coroutines are async. All they do is kind of emulate this behaviour by yielding control.

    Kind Regards,
    Joe.
     
  3. mrSaig

    mrSaig

    Joined:
    Jan 14, 2010
    Posts:
    68
    hi,
    yes they don't run async they just pause execution with the yield control and continue their execution next frame (or whatever yield command you used) exactly at this position.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    Code (CSharp):
    1.  
    2. void Start () {
    3.     StartCoroutine(BigCounter());
    4. }
    5.  
    6. private IEnumerator BigCounter()
    7. {
    8.     for (int i = 0; i < 10000; i++)
    9.     {
    10.         Debug.Log("Hello" + i);
    11.         yield return null; //This goes in here
    12.     }
    13. }
    Note that it's locking for so long because Debug.Log is the most expensive operation there is.


    Coroutines runs synchronously in step with the rest of your code. If you want to work on a different thread, use a thread as normal. The only Unity-specific limitation to threads is that you can't access most of the Unity API from a different thread, as those objects needs to be in sync with the c++ engine.

    You can still totally push a bunch of data to a thread and let it work with it.
     
    Kiwasi and Ryiah like this.
  5. mondeon

    mondeon

    Joined:
    May 29, 2015
    Posts:
    46
    Thanks you all for the quick response!

    My question now is - why is yield return new WaitForSeconds(x) in a Coroutine working async (virtually). I thought I can put the for loop the same way in a second Coroutine and yield return StartCoroutine(SecondCorout()) in the first coroutine, but the result was the same..

    Code (CSharp):
    1. void Start () {
    2.   StartCoroutine(BigCounter());
    3. }
    4.  
    5. private IEnumerator BigCounter(){
    6.   yield return StartCoroutine(Corout2());
    7. }
    8.   private IEnumerator Corout2()
    9.   {
    10.   for (int i = 0; i < 10000; i++)
    11.   {
    12.   Debug.Log("Hello" + i);
    13.   }
    14.   yield return null;
    15.   }
    So, if I want to create an object asynchronously and use C# threads, my big concern is what @Baste said "you can't access most of the Unity API from a different thread".

    Is there a mechanism, that "emulates" multithreading, where I don't have to explicitly tell where to yield and continue execution on next frame?

    In other words, for my example with the counter: putting yield return null INSIDE the for loop will execute every frame EXACTLY one step of the loop. What if Unity "has the time" to execute more loop steps in the same frame, so I don't have to wait that long for the loop to finish? (And at the same time I have access the Unity APIs in that async job..) I am sure there must be a solution to this, since loading big maps for a game or 50 prefabs on loading of game scene should be done without blocking completely the main thread of the game - and there are a lot of mobile games that should be using that solution..
     
  6. ericbegue

    ericbegue

    Joined:
    May 31, 2013
    Posts:
    1,353
    You can think of WaitForSeconds(x) as another couroutine. And it's not "virtually" asynchronous, it's in fact asynchronous, since the process does not block and wait for x second.

    No such mechanism. You are describing a multi-threading scenario.


    We have to (a)wait until Unity updates it's Mono .Net runtime and supports async and await for doing such things. Until then, we have to deal with coroutines or implement our own time sharing system, or use multi-threading without touching the Unity API.
     
    mondeon likes this.
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    WaitForSeconds(x) is actually "Wait for the first frame after x seconds has passed". It just checks a timestamp against the current time on every frame.

    If you want some mechanism to spend the rest of the time left on the current frame to do some work, you'll have to implement that on your own.
     
    mondeon likes this.
  8. mondeon

    mondeon

    Joined:
    May 29, 2015
    Posts:
    46
    Ok, thank you for the extensive information!
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You can get hacky with a stopwatch. But at this stage it's worth questioning if you should make the whole thing properly asynchronous instead.