Search Unity

Coroutines are not magic

Discussion in 'General Discussion' started by Jeff-Kesselman, Nov 18, 2014.

  1. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Sure you can. Basically anything not Unity API, which means most stuff in .NET. I've done it several times; it's quite useful in the right circumstances.

    You don't have to wait for anything in System.IO as far as I know. It's all atomic.

    --Eric
     
  2. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    You can do multi-threading, you just can't touch the Unity API from those other threads. That's a super important distinction.

    A supposed lack of script-side multi-threading has nothing to do with why co-routines are provided. As others have said, they're different tools for different jobs. Coroutines are useful for sequencing. Threads are useful for getting heavy-lifting out of your main execution path.
     
    npsf3000 likes this.
  3. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    I'm the same.
     
  4. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Can you please elaborate?
     
  5. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    So are coroutines.

    No idea what you're referring to here. Native-to-managed transitions? Both Update and coroutines involve that.
     
    deram_scholzara likes this.
  6. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    Again, I didn't say you never use them. Please see the original post.

    BUT the short answer is that at the end of the day pretty much anything that can be done with a yield can also be done with a in-loop status check. Ultimately, thats what is happening anyway.
     
  7. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    http://en.wikipedia.org/wiki/Context_switch

    A context switch involves saving off the complete state of the processing hardware so it can be restored at a later date.

    That's what a co-routine is... an abstraction of a context switch.

    Its the essence of all multi-tasking (as opposed to multi-threading).
     
  8. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    Exactly! Thats the point

    See the above. Restoring a co-routine takes more work then a simple function call because you have to save and restore the entire current processing state.
     
  9. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    A valid point, but outside of the scope of the original post which was why using Co-routines doesn't magically make your code more efficient then doing things in Update.

    BUT if you are going to throw off your own private computation thread then you *probably* don't need co-routines as its not overly likely you are going to be doing tasking within that thread.
     
    Last edited: Nov 20, 2014
  10. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    There seems to be a great deal of wandering of ideas in this thread.

    PLEASE read the original post. It never said that co-routines couldn't be useful in the right places, merely that there is some over-head to them and, most importantly, no inherent performance advantage to moving code from Update to a co-routne.
     
  11. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Not sure I follow. Because I say you can do everything people commonly use coroutines for in another way, I don't understand them?
     
  12. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    They are there but hardly necessary. I wouldn't say they are bad. They are just an option. I still handle these things (states and other complicated multi-frame operations) the way I have for a long time now. If I have anything that needs to do incremental processing over an x number of frames consisting of multiple steps I just use a timer or a timer and step counter. It is very simple and I'd see no need to complicate things by using Coroutines. There may be some specific use cases where they would make things simpler. Then I would use them.
     
    Jeff-Kesselman likes this.
  13. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    Yup. That was my point. Thanks for summing it up so succinctly.

    Where they are the appropriate or useful abstraction, by all means use them.

    But don't use them just 'cause they seem neat or you think getting the work out of the Update call will magically make your program faster.
     
  14. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,422
    I love Coroutines. They aren't magical, never met anyone who thinks otherwise.

    But, in the end it comes down to personal preferences. I like it very much to work with them. I hate to use timer/step counters if I don't have to :p
     
  15. 3agle

    3agle

    Joined:
    Jul 9, 2012
    Posts:
    508
    Those insisting coroutines are complicated should probably just learn what they actually are.
    Sure they are optional, sure you can do things without them, but they are there to make your life easier!
    Not to mention being more readable. (Which is in my opinion far more important than performance)

    Take a simple example of a timer:

    Using Update:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class UpdateTimer : MonoBehaviour {
    6.  
    7.     int timeLimit = 5;
    8.     float currentTime = 0;
    9.     bool updateCompleted = false;
    10.  
    11.     // Update is called once per frame
    12.     void Update ()
    13.     {
    14.         if (!updateCompleted)
    15.         {
    16.             if (currentTime >= timeLimit)
    17.             {
    18.                 updateCompleted = true;
    19.                 print("Update Completed!");
    20.             }
    21.             currentTime += Time.deltaTime;
    22.         }
    23.     }
    24. }
    25.  
    Using a coroutine:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class CoroutineTimer : MonoBehaviour {
    6.  
    7.     int timeLimit = 5;
    8.  
    9.     void Start()
    10.     {
    11.         StartCoroutine(Timer(timeLimit));
    12.     }
    13.  
    14.     public IEnumerator Timer(int timeLimit)
    15.     {
    16.         yield return new WaitForSeconds(timeLimit);
    17.         print("Completed!");
    18.     }
    19. }
    20.  
    In my opinion the coroutine makes for more readable code, is shorter, and is re-usable without any extra work.
     
    angrypenguin likes this.
  16. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    Again, no one is saying co-routines are inherently complicated, to my knowledge.

    However you can over-complicate your code using them inappropriately, as you can with any advanced data structure.

    And we have seen a rash of that over in Answers recently. People who don't really understand what co-routines are or what is or isn't an appropriate use for them trying to shoe horn their code into them out of Update-phobia.

    So, I think this is a straw-man argument, really. No one is arguing they shouldn't be in the language, or that there aren't valid reasons to use them, just that fear of putting things in Update isn't one of them.

    As for readability, to some degree that is always in the eye of the beholder. Co-routines do "hide" some of your processing outside of the main game loop code. That can be a simplifier, or it can be a confuser, again depending on usage and personal preferences.
     
  17. 3agle

    3agle

    Joined:
    Jul 9, 2012
    Posts:
    508
    Not a straw man, just an opinion, as stated.

    And yes, people in this thread have suggested coroutines are more complicated. I merely wanted to show that they are not, in fact, that complex.
     
  18. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    Okay well, it certainly wasn't the point of the thread when I started it :)
     
  19. 3agle

    3agle

    Joined:
    Jul 9, 2012
    Posts:
    508
    Apologies, it was more a response to the general trend of the thread.

    Both methods have their place, but it's important to have the ability to use all of the tools available to you, I think.
    Whether there are people in Answers who misunderstand these tools is not relevant when they could be fairly easily informed of their mistake. Though I wouldn't know this as I don't visit Answers.
     
    angrypenguin likes this.
  20. Jeff-Kesselman

    Jeff-Kesselman

    Joined:
    Apr 5, 2010
    Posts:
    99
    Heh, and the point of my original post was just that... to head off what saw as a persistent misunderstanding :)
     
  21. BFGames

    BFGames

    Joined:
    Oct 2, 2012
    Posts:
    1,543
    I use them a lot... But mostly work on PC games at the moment where the performance different is so little that i really don't care.

    Also i use multithreading a lot for stuff like our voxel world and pathfinding in our game.
     
  22. cl9-2

    cl9-2

    Joined:
    May 31, 2013
    Posts:
    417
    Coroutines generate garbage. I generally prefer timers and InvokeRepeating in Update
     
    RJ-MacReady likes this.
  23. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Yep. Coroutines aren't about what is being done, but *how* the developer thinks about the problem. Coroutines are to Update as Loops are to Goto.
     
  24. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Still don't follow. Are you saying that Update is ancient and outdated and for an entirely different programming methodology? Because as I see it, Update and coroutines are both modern tools with different uses. The problem is that people fail to differentiate between those uses properly, which leads to things like this post is about, where excessive and improper use of coroutines as some sort of replacement for Update creates problems you wouln't have with a normal, component-based flow.

    It's less like comparing loops and GOTO, and more like comparing an array to a list. Different, though similar, tools for different applications.
     
  25. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    I would say a lot of the issue is that people are introduced to coroutines way before they should be. People get introduced to them mostly because they are an easy way to deal with time, but they end up abusing them before they are really at the point to understand the architecture. Add in some mixed messages about update and it's a bit of a recipe for disaster.
     
  26. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Hence the reason why I can't think of a use for one. I only use what is necessitated by the situation, so if I benefit from 1/10 of the potential benefits, but the other 9/10 potential benefits don't apply, I'll opt with something more vanilla. I hate, and I mean hate, when programmers user features just to user them because they seem cool.
     
  27. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I suspect it's that a lot of these people are learning "game programming with [insert tool name]" instead of learning programming and game development as distinct skills. Instead of learning how programming is used to manage and operate on data and then understanding how that relates to building games, they try to learn how programming can make stuff happen on the screen. That leaves some fundamental gaps.
     
    ManAmazin, Ryiah and Trigve like this.
  28. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Won't totally disagree with that, although I'm prone to think it's more of an trap for people who have the basics down.
     
  29. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    That comparison also works. Using it as an example for my earlier remarks if someone was to say that they can do everything a List can do easily with Array's what would you think?
     
  30. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    How many features of the list do you need for what you're doing? How do you plan on accessing elements? Are the elements object references that are going to change order, or is this just a collection of values? Are you modeling an actual list of things, like a laundry list? Or a container of things where you have a finite number of storage compartments where some elements can be empty (a pill box comes to mind).

    In short, a lot.
     
    Last edited: Nov 21, 2014
  31. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    What a long discussion about something so simple. If a person wants to use a coroutines then let them use coroutines. If a person does not want to use them let them not use them.

    It is just a matter of preference between whether a programmer wants to explicitly implement something or use the "magic" implementation offered by coroutines. Hiding the implementation can be seen as adding complexity to the program. However, it also simplifies the actual implementation because you no longer need to handle the "overhead" bits and instead can focus on only the desired result.

    From the Unity reference:
    As an example, consider the task of gradually reducing an object’s alpha (opacity) value until it becomes completely invisible.
    Code (CSharp):
    1. IEnumerator Fade()
    2. {
    3.     for (float f = 1f; f >= 0; f -= 0.1f)
    4.    {
    5.         Color c = renderer.material.color;
    6.         c.a = f;
    7.         renderer.material.color = c;
    8.         yield return null;
    9.     }
    10. }
    And you need to start it:
    Code (CSharp):
    1. void Update()
    2. {
    3.     if (Input.GetKeyDown("f")) {
    4.         StartCoroutine("Fade");
    5.     }
    6. }

    The way I'd do this (without coroutines):
    Code (CSharp):
    1. // Set up flag at the script / class level
    2. private bColorFadeActive = false;
    Code (CSharp):
    1. private void Fade()
    2. {
    3.     Color cRendererColor = renderer.material.color;
    4.     if (cRendererColor.a < 0.1F)
    5.     {
    6.         bColorFadeActive = false;
    7.         return;
    8.     }
    9.  
    10.     cRendererColor.a -= 0.1f;
    11.     renderer.material.color = cRendererColor;
    12. }
    And, of course, you'd need to start it and also for this explicit implementation you need to call it:
    Code (CSharp):
    1. void Update()
    2. {
    3.     if (Input.GetKeyDown("f")) {
    4.         cRendererColor.a = 1F;
    5.         bColorFadeActive = true;
    6.     }
    7.  
    8.     if (bColorFadeActive)
    9.         Fade();
    10. }
    Apologize for mistakes. I am typing this into the code box here in the post.

    Anyway, neither way is better than the other. It just depends on if you want to explicitly handle such things or rely on the coroutines to do it. I am so used to doing this stuff that I never considered using coroutines. I mean it is all so simple anyway and I prefer the explicit implementation because I feel it makes it clearer exactly what is going on. And I feel more confident in the explicit implementation because it is my code. Over the years, I have found there are far less problems when you use less of an API. You reduce the amount of glitches that can occur that have nothing to do with your own code and are instead a bug in the API method you are using. I hate those!

    On the other hand using the coroutine, although hiding some implementation details, does simplify the actual code. There is less code to type. Less stuff to manage and that is a benefit as well. As long as it actually works bug free and you understand any limitations and so forth of their implementation.

    So, in summary. Of course, there are benefits to using coroutines. And there are benefits of not using them. Which way you use will depend on which things are most important to you.
     
    Last edited: Nov 21, 2014
    RJ-MacReady likes this.
  32. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Yeah, but... but...

    This is one of those "my way is the right way" things.
     
  33. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Which would be nice if the statement was 'in this situation should I use a List or Array?' However the statement I was countering was along the lines that everything a List can do an Array can do easily.
     
  34. Stoven

    Stoven

    Joined:
    Jul 28, 2014
    Posts:
    171
    I love Coroutines. There are some 'gotchas' when using them that you have to watch out for, but there are workarounds if you use the Wrapper/Sandwiching technique. You can even hand a custom-made class that inherits from IEnumerator to be run on a Coroutine and set the Current object value of that custom class to be whatever you want to yield upon.

    I only wish for the following 2 features when it comes to Coroutines:

    -For YieldInstruction to have a IsCurrentlyRunning property that simply returns true or false if the Coroutine is still running. Just that one simple thing would open a lot of windows for easily extending the use of Coroutines.

    -Serializable Coroutines (If this is already doable, then awesome. I tried to check if Coroutines were Serializable using methods in System, but I didn't realize until later that even if something isn't Serializable with System calls, it could be handled in special cases by Unity's Serialization such as Monobehaviours, ScriptableObjects, etc).
     
    Last edited: Nov 21, 2014
  35. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Still is nice. You can do anything a list can do with an array. You can do anything an array can do with pointer operations and value types. You can do any of that in assembler, etc.

    How do we know what to use??? <--- That's the million dollar question. Use what fits the best. You are being very vague, as well. "Can you do x with y? Why wouldn't you just use z, instead of using y?"

    My response is such because I've never thought of a list as a list, I've always been trying to solve some abstract problem so the list becomes "Enemies in the field" and conceptually that's how you think of it.

    So, I think ultimately you're venturing into a weird place of highly technical arguments that ignore the intended use of all these standard library features in the first place, which is to be used.

    But yeah, I guess if all I was trying to achieve was having the operations of a List I would just declare a List.
     
  36. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    So you've built a simple state machine where you:
    • Create a class level value.
    • Poll an event, if true set said value to true
    • Every tick if that value is true, perform part of an action.
    • When finished set the trigger to false.
    Which is fair enough - there's nothing wrong with your code. However, what I'd try to do is:
    • Poll an event
    • Perform an action.
    Here is what I'd like to write:
    Code (CSharp):
    1.  
    2. void Start()
    3. {  
    4.      StartCoroutine(Fade());  //Alternatively fade could be inlined into start e.g. if this was a Fade Component.
    5. }
    6.  
    7. IEnumerator Fade()
    8. {
    9.     while (true) {  //If looping is desired
    10.         while (!Input.GetKeyDown("f")) yield return null;  //Wait for event
    11.         for (float f = 1f; f >= 0; f -= 0.1f)  //Perform Action
    12.         {
    13.              Color c = renderer.material.color;
    14.              c.a = f;
    15.              renderer.material.color = c;
    16.              yield return null;
    17.         }
    18.    }
    19. }
    It's short, self-contained (no state variables to worry about, not spread over several functions), and explicit in the flow.
     
    Last edited: Nov 21, 2014
    Ryiah likes this.
  37. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Your way is better. :)

    I think that's your point.
     
  38. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Except it isn't "better". My code isn't "better". The Unity example code isn't "better". That code is apparently better for @npsf3000, my code is better for me and Unity's example code may or may not be better for whoever wrote that example. ;)
     
  39. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    He thinks it is, and that's the point I think he's been expressing.

    Nobody knows "best", that's the point I'm expressing... that, and that I think if there's a good use for a coroutine I'll be the first to sieze it and exploit it. But so far I haven't come across a good fit for it. Who knows, that could change tomorrow.
     
    GarBenjamin likes this.
  40. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Nope, that's not my point at all.

    My point is that the most important differences between Coroutines and Update, or List and Array or Loop and Goto are not the result or technical implementation. The most important differences are in how they help a programmer express the problem they are solving in a clear, concise way with minimal chance of error.

    If I see a coroutine I know immediately that the developer is managing a complex state machine that exists over time with other code. If I see an update I know immediately that the developer is performing a fixed set of actions every tick. This is invaluable information.

    I'm not saying which solution is best for which application, but that the use of different structures can give vital information about the *intent* of the code. If I only used Update for example, then I've lost this knowledge.

    If you're going to go into the issues of what is better then I'd point out that the statement that 'your code is in fact better for you' is actually a testable assumption, have you tested this theory?
     
  41. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Time Spent Developing = Total Time - Time Spent Arguing About Developing.

    Yes, I've tested that theory.
     
    dterbeest, Cogent and GarBenjamin like this.
  42. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Sure. I have used this method for a long time now. It works. Gives explicit understanding what is going on. Provides direct easy control to pause or stop the fade early. And it is such a trivial thing in my mind it needs no more discussion.
     
  43. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    Exactly. Some people spend years studying a grain of sand or an ant and enjoy debating various aspects. I can appreciate the wonder they find in such things but I don't share their interest.
     
    RJ-MacReady likes this.
  44. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    But I still don't think we fully explored whether or not coroutines are actually rooted in magic... although, I sense strange goings on in the forests to the east...
     
  45. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Actually that question was directed at GarBenjamin.

    I was trying to figure out how you turned a discussion from the differences between two paradigms in programming (e.g. comparisons of high level concepts vs low level implementation) into a trivial discussion of what is 'better'.
     
  46. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Give me some credit, I've actually turned it into a discussion about magic. *waves hands cryptically*

    Edit: which, ironically, means I'm more on topic than you.
     
  47. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Now it's a discussion about your attempt to turn the discussion to magic.
     
  48. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    Well.. are you saying that they are or aren't? Stick to the topic.
     
  49. flaminghairball

    flaminghairball

    Joined:
    Jun 12, 2008
    Posts:
    868
    I think the difficulty we're having here is that some folks are arguing about whether coroutines are better than update, and some folks are arguing about whether good programmers are better than not-good programmers.

    Understanding how coroutines work makes you a better programmer - no argument here. Understanding how Update works makes you a better programmer. Understanding when to use each paradigm makes you a great programmer.

    Before my days in C#, I had no concept of LINQ. After I delved into the more complex features of C# and got to LINQ/lambdas, my code became much more flexible, reliable, and expressive. This is not because LINQ exists - heck, most of it could be re-implemented in a lot of languages in a day - it's because LINQ taught me how to think about code and data in a different way - not a better way, but a different way. This is valuable no matter what language or toolset I'm working in.
     
    npsf3000 and andymads like this.
  50. RJ-MacReady

    RJ-MacReady

    Joined:
    Jun 14, 2013
    Posts:
    1,718
    I think this "argument" has become about there being a better/best way to do things, not just updates vs. coroutines. Which is silly, because the original point was that a coroutine is not a parallel thread running in the background which would allows you to eliminate slowdowns by throwing everything in coroutines. It's just normal line-by-line execution per frame, where the yield statement serves as a pause in execution until the next frame. It's actually, if anything, slower than just running the same script in Update, but by a negligible amount.

    Then it became about why coroutines rock n' roll. Which then became about how anyone who doesn't use coroutines is a fool that deserves pity. Which then becomes COROUTINES ARE THE ONLY WAY THE BURDEN OF PROOF IS ON YOU TO PROVE OTHERWISE. Which is when I was like....



    And now u no.
     
    Last edited: Nov 21, 2014