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

Is using while(true) in Coroutine bad idea?

Discussion in 'Scripting' started by ScamTheMan, Oct 3, 2020.

  1. ScamTheMan

    ScamTheMan

    Joined:
    Oct 4, 2018
    Posts:
    75
    The title tells pretty much the story.

    I wanted to make a simple diode switching on and off whole time playing and I made the code like this
    Code (CSharp):
    1. public IEnumerator IENUM()
    2.     {
    3.         while (true)
    4.         {
    5.             for (int x = 0; x <= 6; x += 2)
    6.             {
    7.                  //Do the stuff with delay
    8.             }
    9.         }
    10.     }
    My question is, will this affect the game in any way like slowing performance? The game is going to be in this scene only and I don't want the game to be laggy after an hour or so by e.g. creating too many Coroutines or something. I didn't see anyone use it this way while searching on different forums.

    Thank you.
     
  2. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    It's fine; the yield instructions in the coroutine will prevent the game from freezing, if that's what you're worried about.
    That said though, if this coroutine is meant to just be running endlessly forever, why not just use
    Update
    instead?
     
  3. ScamTheMan

    ScamTheMan

    Joined:
    Oct 4, 2018
    Posts:
    75
    Making delay with float and reseting it will be too much of a hassle, this way the yield will do it for me. Thank you for your response.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,766
    I agree with this use case; sometimes it's just easier to express things this way.

    However, let me point out one thing that can bite you with loops inside of infinite loops:

    If you do not provide at least SOME kind of yield and allow it to go compute bound, this will lock up Unity.

    For instance:

    Code (csharp):
    1. // cycle through aliens, speeding each one up every second or so
    2. while(true)
    3. {
    4.   foreach (var alien in alienList.ToArray())
    5.   {
    6.     if (alien) alien.speedUpALittleBit();
    7.     yield return new WaitForSeconds( 1.0f);
    8.   }
    9. }
    This code will work fine until someone removes every alien from the alienList and then ... magical lockup.

    ALSO: keep in mind if someone sets this GameObject to inactive, then turns it back on, ALL coroutines running on it will STOP PERMANENTLY. This would not be the case if you tracked the time delay with Update().
     
    SeerSucker69, vidanov and Bunny83 like this.
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,531
    Yes, this is a common oversight by some developers. Of course you have to ensure that the infinite loop will always at least after a determind time hit a yield instruction. Also keep in mind while some code is executing inside the coroutine, no other code will run until you hit a yield statement. Coroutines do not run asynchronously but synchronously. It's cooperative multi tasking. If you do not cooperate (i.e. give the control back to Unity) you will be trapped inside your code and your application will freeze / hang.

    To answer the actual question: Using an endlessly running coroutine is definitely better than starting / stopping coroutines all the time. Starting a coroutine requires an object allocation. Once it's created and running it doesn't allocate any additional memory (except any allocations you do inside your coroutine of course). So I only see two main usages for coroutines: Either you have rare one-time events like scene fade out / in animations or maybe some download process at the game start. The other kind of coroutines are the infinite running coroutines. Infinite looping coroutines have many advantages. They are quire useful for "modal" behaviour. So you can do something like

    Code (CSharp):
    1. IEnumerator MyCoroutine()
    2. {
    3.     while(true)
    4.     {
    5.         while (!Input.GetKeyDown(KeyCode.Space))
    6.             yield return null;
    7.        
    8.         // some heavy task or process that requires splitting / spreading over multiple frames
    9.         // or just waiting for other things
    10.     }
    11. }
    12.  
    The advantage is that while the process is running, you don't have to care about blocking the triggering key to avoid multiple calls. A common usecase might be a dialog that could be implemented like this
     
    Last edited: Oct 4, 2020
    Kurt-Dekker likes this.
  6. mattseaton22

    mattseaton22

    Joined:
    Sep 12, 2019
    Posts:
    43
    I wanted to use WaitUntil, but I was worried about an infinite loop so I wrote my own yield instruction that times out if it takes too long.

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4.  
    5.     public class TimedWait : CustomYieldInstruction
    6.     {
    7.         float m_TimeStart = 0f;
    8.         float m_MaxWait = 10f;
    9.         Func<bool> m_Predicate;
    10.  
    11.         public override bool keepWaiting
    12.         {
    13.             get
    14.             {
    15.                 return !m_Predicate() && (Time.unscaledTime - m_TimeStart) <= m_MaxWait;
    16.             }
    17.         }
    18.  
    19.         public TimedWait(Func<bool> condition, float maxWait)
    20.         {
    21.             m_MaxWait = maxWait;
    22.             m_TimeStart = Time.unscaledTime;
    23.             m_Predicate = condition;
    24.         }
    25.     }
    26.  
    You would use it like this
    Code (CSharp):
    1. IEnumerator SomeCroutine()
    2. {
    3.     yield return new TimedWait(() => someBool, floatTimeout);
    4. }