How does StartCoroutine / yield return pattern really work in Unity?

C#Unity3dCoroutine

C# Problem Overview


I understand the principle of coroutines. I know how to get the standard StartCoroutine / yield return pattern to work in C# in Unity, e.g. invoke a method returning IEnumerator via StartCoroutine and in that method do something, do yield return new WaitForSeconds(1); to wait a second, then do something else.

My question is: what's really going on behind the scenes? What does StartCoroutine really do? What IEnumerator is WaitForSeconds returning? How does StartCoroutine return control to the "something else" part of the called method? How does all this interact with Unity's concurrency model (where lots of things are going on at the same time without use of coroutines)?

C# Solutions


Solution 1 - C#

The oft referenced Unity3D coroutines in detail link is dead. Since it is mentioned in the comments and the answers I am going to post the contents of the article here. This content comes from this mirror.


>Unity3D coroutines in detail

>Many processes in games take place over the course of multiple frames. You’ve got ‘dense’ processes, like pathfinding, which work hard each frame but get split across multiple frames so as not to impact the framerate too heavily. You’ve got ‘sparse’ processes, like gameplay triggers, that do nothing most frames, but occasionally are called upon to do critical work. And you’ve got assorted processes between the two.

>Whenever you’re creating a process that will take place over multiple frames – without multithreading – you need to find some way of breaking the work up into chunks that can be run one-per-frame. For any algorithm with a central loop, it’s fairly obvious: an A* pathfinder, for example, can be structured such that it maintains its node lists semi-permanently, processing only a handful of nodes from the open list each frame, instead of trying to do all the work in one go. There’s some balancing to be done to manage latency – after all, if you’re locking your framerate at 60 or 30 frames per second, then your process will only take 60 or 30 steps per second, and that might cause the process to just take too long overall. A neat design might offer the smallest possible unit of work at one level – e.g. process a single A* node – and layer on top a way of grouping work together into larger chunks – e.g. keep processing A* nodes for X milliseconds. (Some people call this ‘timeslicing’, though I don’t).

>Still, allowing the work to be broken up in this way means you have to transfer state from one frame to the next. If you’re breaking an iterative algorithm up, then you’ve got to preserve all the state shared across iterations, as well as a means of tracking which iteration is to be performed next. That’s not usually too bad – the design of an ‘A* pathfinder class’ is fairly obvious – but there are other cases, too, that are less pleasant. Sometimes you’ll be facing long computations that are doing different kinds of work from frame to frame; the object capturing their state can end up with a big mess of semi-useful ‘locals,’ kept for passing data from one frame to the next. And if you’re dealing with a sparse process, you often end up having to implement a small state machine just to track when work should be done at all.

>Wouldn’t it be neat if, instead of having to explicitly track all this state across multiple frames, and instead of having to multithread and manage synchronization and locking and so on, you could just write your function as a single chunk of code, and mark particular places where the function should ‘pause’ and carry on at a later time?

>Unity – along with a number of other environments and languages – provides this in the form of Coroutines.

>How do they look? In “Unityscript” (Javascript):

function LongComputation()
{
    while(someCondition)
    {
        /* Do a chunk of work */
 
        // Pause here and carry on next frame
        yield;
    }
}

>In C#:

IEnumerator LongComputation()
{
    while(someCondition)
    {
        /* Do a chunk of work */
 
        // Pause here and carry on next frame
        yield return null;
    }
}

>How do they work? Let me just say, quickly, that I don’t work for Unity Technologies. I’ve not seen the Unity source code. I’ve never seen the guts of Unity’s coroutine engine. However, if they’ve implemented it in a way that is radically different from what I’m about to describe, then I’ll be quite surprised. If anyone from UT wants to chime in and talk about how it actually works, then that’d be great.

>The big clues are in the C# version. Firstly, note that the return type for the function is IEnumerator. And secondly, note that one of the statements is yield return. This means that yield must be a keyword, and as Unity’s C# support is vanilla C# 3.5, it must be a vanilla C# 3.5 keyword. Indeed, here it is in MSDN – talking about something called ‘iterator blocks.’ So what’s going on?

>Firstly, there’s this IEnumerator type. The IEnumerator type acts like a cursor over a sequence, providing two significant members: Current, which is a property giving you the element the cursor is presently over, and MoveNext(), a function that moves to the next element in the sequence. Because IEnumerator is an interface, it doesn’t specify exactly how these members are implemented; MoveNext() could just add one toCurrent, or it could load the new value from a file, or it could download an image from the Internet and hash it and store the new hash in Current… or it could even do one thing for the first element in the sequence, and something entirely different for the second. You could even use it to generate an infinite sequence if you so desired. MoveNext() calculates the next value in the sequence (returning false if there are no more values), and Current retrieves the value it calculated.

>Ordinarily, if you wanted to implement an interface, you’d have to write a class, implement the members, and so on. Iterator blocks are a convenient way of implementing IEnumerator without all that hassle – you just follow a few rules, and the IEnumerator implementation is generated automatically by the compiler.

>An iterator block is a regular function that (a) returns IEnumerator, and (b) uses the yield keyword. So what does the yield keyword actually do? It declares what the next value in the sequence is – or that there are no more values. The point at which the code encounters a yield return X or yield break is the point at which IEnumerator.MoveNext() should stop; a yield return X causes MoveNext() to return true andCurrent to be assigned the value X, while a yield break causes MoveNext() to return false.

>Now, here’s the trick. It doesn’t have to matter what the actual values returned by the sequence are. You can call MoveNext() repeatly, and ignore Current; the computations will still be performed. Each time MoveNext() is called, your iterator block runs to the next ‘yield’ statement, regardless of what expression it actually yields. So you can write something like:

IEnumerator TellMeASecret()
{
  PlayAnimation("LeanInConspiratorially");
  while(playingAnimation)
    yield return null;
 
  Say("I stole the cookie from the cookie jar!");
  while(speaking)
    yield return null;
 
  PlayAnimation("LeanOutRelieved");
  while(playingAnimation)
    yield return null;
}

>and what you’ve actually written is an iterator block that generates a long sequence of null values, but what’s significant is the side-effects of the work it does to calculate them. You could run this coroutine using a simple loop like this:

IEnumerator e = TellMeASecret();
while(e.MoveNext()) { }

>Or, more usefully, you could mix it in with other work:

IEnumerator e = TellMeASecret();
while(e.MoveNext()) 
{ 
  // If they press 'Escape', skip the cutscene
  if(Input.GetKeyDown(KeyCode.Escape)) { break; }
}

>It’s all in the timing As you’ve seen, each yield return statement must provide an expression (like null) so that the iterator block has something to actually assign to IEnumerator.Current. A long sequence of nulls isn’t exactly useful, but we’re more interested in the side-effects. Aren’t we?

>There’s something handy we can do with that expression, actually. What if, instead of just yielding null and ignoring it, we yielded something that indicated when we expect to need to do more work? Often we’ll need to carry straight on the next frame, sure, but not always: there will be plenty of times where we want to carry on after an animation or sound has finished playing, or after a particular amount of time has passed. Those while(playingAnimation) yield return null; constructs are bit tedious, don’t you think?

>Unity declares the YieldInstruction base type, and provides a few concrete derived types that indicate particular kinds of wait. You’ve got WaitForSeconds, which resumes the coroutine after the designated amount of time has passed. You’ve got WaitForEndOfFrame, which resumes the coroutine at a particular point later in the same frame. You’ve got the Coroutine type itself, which, when coroutine A yields coroutine B, pauses coroutine A until after coroutine B has finished.

>What does this look like from a runtime point of view? As I said, I don’t work for Unity, so I’ve never seen their code; but I’d imagine it might look a little bit like this:

List<IEnumerator> unblockedCoroutines;
List<IEnumerator> shouldRunNextFrame;
List<IEnumerator> shouldRunAtEndOfFrame;
SortedList<float, IEnumerator> shouldRunAfterTimes;
 
foreach(IEnumerator coroutine in unblockedCoroutines)
{
    if(!coroutine.MoveNext())
        // This coroutine has finished
        continue;
 
    if(!coroutine.Current is YieldInstruction)
    {
        // This coroutine yielded null, or some other value we don't understand; run it next frame.
        shouldRunNextFrame.Add(coroutine);
        continue;
    }
 
    if(coroutine.Current is WaitForSeconds)
    {
        WaitForSeconds wait = (WaitForSeconds)coroutine.Current;
        shouldRunAfterTimes.Add(Time.time + wait.duration, coroutine);
    }
    else if(coroutine.Current is WaitForEndOfFrame)
    {
        shouldRunAtEndOfFrame.Add(coroutine);
    }
    else /* similar stuff for other YieldInstruction subtypes */
}
 
unblockedCoroutines = shouldRunNextFrame;

>It’s not difficult to imagine how more YieldInstruction subtypes could be added to handle other cases – engine-level support for signals, for example, could be added, with a WaitForSignal("SignalName")YieldInstruction supporting it. By adding more YieldInstructions, the coroutines themselves can become more expressive – yield return new WaitForSignal("GameOver") is nicer to read thanwhile(!Signals.HasFired("GameOver")) yield return null, if you ask me, quite apart from the fact that doing it in the engine could be faster than doing it in script.

>A couple of non-obvious ramifications There’s a couple of useful things about all this that people sometimes miss that I thought I should point out.

>Firstly, yield return is just yielding an expression – any expression – and YieldInstruction is a regular type. This means you can do things like:

YieldInstruction y;
 
if(something)
 y = null;
else if(somethingElse)
 y = new WaitForEndOfFrame();
else
 y = new WaitForSeconds(1.0f);
 
yield return y;

>The specific lines yield return new WaitForSeconds(), yield return new WaitForEndOfFrame(), etc, are common, but they’re not actually special forms in their own right.

>Secondly, because these coroutines are just iterator blocks, you can iterate over them yourself if you want – you don’t have to have the engine do it for you. I’ve used this for adding interrupt conditions to a coroutine before:

IEnumerator DoSomething()
{
  /* ... */
}
 
IEnumerator DoSomethingUnlessInterrupted()
{
  IEnumerator e = DoSomething();
  bool interrupted = false;
  while(!interrupted)
  {
    e.MoveNext();
    yield return e.Current;
    interrupted = HasBeenInterrupted();
  }
}

>Thirdly, the fact that you can yield on other coroutines can sort of allow you to implement your own YieldInstructions, albeit not as performantly as if they were implemented by the engine. For example:

IEnumerator UntilTrueCoroutine(Func fn)
{
   while(!fn()) yield return null;
}
 
Coroutine UntilTrue(Func fn)
{
  return StartCoroutine(UntilTrueCoroutine(fn));
}
 
IEnumerator SomeTask()
{
  /* ... */
  yield return UntilTrue(() => _lives < 3);
  /* ... */
}

>however, I wouldn’t really recommend this – the cost of starting a Coroutine is a little heavy for my liking.

>Conclusion I hope this clarifies a little some of what’s really happening when you use a Coroutine in Unity. C#’s iterator blocks are a groovy little construct, and even if you’re not using Unity, maybe you’ll find it useful to take advantage of them in the same way.

Solution 2 - C#

The first heading below is a straight answer to the question. The two headings after are more useful for the everyday programmer.

Possibly Boring Implementation Details of Coroutines

Coroutines are explained in Wikipedia and elsewhere. Here I'll just provide some details from a practical point of view. IEnumerator, yield, etc. are C# language features that are used for somewhat of a different purpose in Unity.

To put it very simply, an IEnumerator claims to have a collection of values that you can request one by one, kind of like a List. In C#, a function with a signature to return an IEnumerator does not have to actually create and return one, but can let C# provide an implicit IEnumerator. The function then can provide the contents of that returned IEnumerator in the future in a lazy fashion, through yield return statements. Every time the caller asks for another value from that implicit IEnumerator, the function executes till the next yield return statement, which provides the next value. As a byproduct of this, the function pauses until the next value is requested.

In Unity, we don't use these to provide future values, we exploit the fact that the function pauses. Because of this exploitation, a lot of things about coroutines in Unity do not make sense (What does IEnumerator have to do with anything? What is yield? Why new WaitForSeconds(3)? etc.). What happens "under the hood" is, the values you provide through the IEnumerator are used by StartCoroutine() to decide when to ask for the next value, which determines when your coroutine will unpause again.

Your Unity Game is Single Threaded (*)

Coroutines are not threads. There is one main loop of Unity and all those functions that you write are being called by the same main thread in order. You can verify this by placing a while(true); in any of your functions or coroutines. It will freeze the whole thing, even the Unity editor. This is evidence that everything runs in one main thread. This link that Kay mentioned in his above comment is also a great resource.

(*) Unity calls your functions from one thread. So, unless you create a thread yourself, the code that you wrote is single threaded. Of course Unity does employ other threads and you can create threads yourself if you like.

A Practical Description of Coroutines for Game Programmers

Basically, when you call StartCoroutine(MyCoroutine()), it's exactly like a regular function call to MyCoroutine(), until the first yield return X, where X is something like null, new WaitForSeconds(3), StartCoroutine(AnotherCoroutine()), break, etc. This is when it starts differing from a function. Unity "pauses" that function right at that yield return X line, goes on with other business and some frames pass, and when it's time again, Unity resumes that function right after that line. It remembers the values for all the local variables in the function. This way, you can have a for loop that loops every two seconds, for example.

When Unity will resume your coroutine depends on what X was in your yield return X. For example, if you used yield return new WaitForSeconds(3);, it resumes after 3 seconds have passed. If you used yield return StartCoroutine(AnotherCoroutine()), it resumes after AnotherCoroutine() is completely done, which enables you to nest behaviors in time. If you just used a yield return null;, it resumes right at the next frame.

Solution 3 - C#

It couldn't be simpler:

Unity (and all game engines) are frame based.

The whole entire point, the whole raison d'etre of Unity, is that it is frame based. The engine does things "each frame" for you. (Animates, renders objects, does physics, and so on.)

You might ask .. "Oh, that's great. What if I want the engine to do something for me each frame? How do I tell the engine to do such-and-such in a frame?"

The answer is ...

That's exactly what a "coroutine" is for.

It's just that simple.

A note on the "Update" function...

Quite simply, anything you put in "Update" is done every frame. It's literally exactly the same, no difference at all, from the coroutine-yield syntax.

void Update()
 {
 this happens every frame,
 you want Unity to do something of "yours" in each of the frame,
 put it in here
 }

...in a coroutine...
 while(true)
 {
 this happens every frame.
 you want Unity to do something of "yours" in each of the frame,
 put it in here
 yield return null;
 }

There is absolutely no difference.

Threads have utterly no connection to frames/coroutines, in any way. There is no connection whatsoever.

The frames in a game engine have utterly no connection to threads, in any way. They are completely, totally, utterly, unrelated issues.

(You often hear that "Unity is single-threaded!" Note that even that statement is very confused. Frames/coroutines just have absolutely no connection at all to threading. If Unity was multithreaded, hyperthreaded, or ran on a quantum computer!! ... it would just have no connection whatsoever to frames/coroutines. It is a completely, totally, absolutely, unrelated issue.)

If Unity was multithreaded, hyperthreaded, or ran on a quantum computer!! ... it would just have no connection whatsoever to frames/coroutines. It is a completely, totally, absolutely, unrelated issue.

So in summary...

So, Coroutines/yield are simply how you access the frames in Unity. That's it.

(And indeed, it's absolutely the same as the Update() function provided by Unity.)

That's all there is to it, it's that simple.

Why IEnumerator?

Couldn't be simpler: IEnumerator returns things "over and over".

(That list of things can either have a specific length, such as "10 things," or the list can go on forever.)

Thus, self-evidently, an IEnumerator is what you would use.

Anywhere in .Net you want to "return over and over," IEnumerator exists for this purpose.

All frame-based computing, with .Net, of course uses IEnumerator to return each frame. What else could it use?

(If you are new to C#, note that IEnumerator is also used for returning "ordinary" things one by one, such as simply the items in an array, etc.)

Solution 4 - C#

Have dig into this lately, wrote a post here - http://eppz.eu/blog/understanding-ienumerator-in-unity-3d/ - that shed a light on the internals (with dense code examples), the underlying IEnumerator interface, and how it is used for coroutines.

>Using collection enumerators for this purpose still seems a bit weird for me. It is the inverse of what enumerators feels designed for. The point of enumerators is the returned value on every access, but the point of Coroutines is the code in-between the value returns. The actual returned value is pointless in this context.

Solution 5 - C#

On Unity 2017+, you can use the native C# async/await keywords for async code, but before that, C# had no native way to implement async code.

Unity had to use a workaround for async code. They achieved this by exploiting the C# iterators, which was a popular async technique at the time.

A look at C# Iterators

Let's say you have this code:

IEnumerable SomeNumbers() {
  yield return 3;
  yield return 5;
  yield return 8;
}

If you run it through a loop, calling as if was an array, you will get 3 5 8:

// Output: 3 5 8
foreach (int number in SomeNumbers()) {
  Console.Write(number);
}

If you are not familiar with iterators (most languages have them to implement lists and collections), they work as an array. The difference is that a callback generates the values.

How do they work?

When looping through an iterator on C#, we use MoveNext to go to the next value.

In the example, we are using foreach, which calls this method under the hood.

When we call MoveNext, the iterator executes everything until its next yield. The parent caller gets the value returned by yield. Then, the iterator code pauses, waiting for the next MoveNext call.

Because of their "lazy" capability, C# programmers used iterators to run async code.

Asynchronous Programming in C# using Iterators

Before 2012, using iterators was a popular hack to perform asynchronous operations in C#.

Example - Asynchronous download function:

IEnumerable DownloadAsync(string URL) {
  WebRequest  req      = HttpWebRequest.Create(url);
  WebResponse response = req.GetResponseAsync();
  yield return response;

  Stream resp = response.Result.GetResponseStream();
  string html = resp.ReadToEndAsync().ExecuteAsync();
  yield return html;

  Console.WriteLine(html.Result);
}

PS: The code above is from this excellent, yet old, article about Async programming using iterators: http://tomasp.net/blog/csharp-async.aspx/

Should I use async instead of StartCoroutine?

As for 2021, the official Unity docs use coroutines on their examples and not async.

Also, the community seems to be more in favor of coroutines instead of async:

  • Developers are familiar with coroutines;
  • Coroutines are integrated with Unity;
  • And others;

I recommend this Unity lecture from 2019, "Best practices: Async vs. coroutines - Unite Copenhagen 2019": https://youtu.be/7eKi6NKri6I


PS: This is an old question from 2012, but I'm answering it because it is still relevant in 2021.

Solution 6 - C#

The basis functions in Unity that you get automatically are the Start() function and the Update() function, so Coroutine's are essentially functions just like the Start() and Update() function. Any old function func() can be called the same way a Coroutine can be called. Unity has obviously set certain boundaries for Coroutines that make them different than regular functions. One difference is instead of

  void func()

You write

  IEnumerator func()

for coroutines. And the same way you can control the time in normal functions with code lines like

  Time.deltaTime

A coroutine has a specific handle on the way time can be controlled.

  yield return new WaitForSeconds();

Although this is not the only thing possible to do inside of an IEnumerator/Coroutine, it is one of the useful things that Coroutines are used for. You would have to research Unity's scripting API to learn other specific uses of Coroutines.

Solution 7 - C#

StartCoroutine is a method to call a IEnumerator function. It is similar to just calling a simple void function, just the difference is that you use it on IEnumerator functions. This type of function is unique as it can allow you to use a special yield function, note that you must return something. Thats as far as I know. Here I wrote a simple flicker game Over text method in unity

    public IEnumerator GameOver()
{
    while (true)
    {
        _gameOver.text = "GAME OVER";
        yield return new WaitForSeconds(Random.Range(1.0f, 3.5f));
        _gameOver.text = "";
        yield return new WaitForSeconds(Random.Range(0.1f, 0.8f));
    }
}

I then called it out of the IEnumerator itself

    public void UpdateLives(int currentlives)
{
    if (currentlives < 1)
    {
        _gameOver.gameObject.SetActive(true);
        StartCoroutine(GameOver());
    }
}

As you can see how I used the StartCoroutine() method. Hope I helped somehow. I am a begainner myself, so if you correct me, or apprecite me, any type of a feedback would be great.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGhopper21View Question on Stackoverflow
Solution 1 - C#James McMahonView Answer on Stackoverflow
Solution 2 - C#Gazihan AlankusView Answer on Stackoverflow
Solution 3 - C#FattieView Answer on Stackoverflow
Solution 4 - C#Geri BorbásView Answer on Stackoverflow
Solution 5 - C#Daniel LoureiroView Answer on Stackoverflow
Solution 6 - C#AlexhawkburrView Answer on Stackoverflow
Solution 7 - C#Deepanshu MishraView Answer on Stackoverflow