yield multiple Coroutines asynchronously

I’ve found this code on a project and after doing a lot of research, it isn’t clear if it’s safe to run and save a coroutine and then check if it’s done.

var routines = new []
{
    StartCoroutine(CoroutineA),
    StartCoroutine(CoroutineB),
    StartCoroutine(CoroutineC)
};

foreach (var routine in routines) yield return routine;

I know that doing “yield return MyIEnumerator” it’s almost the same as doing “yield return StartCoroutine(MyIEnumerator)”, but what about “yield return MyRunningCoroutine”.

I suppose you could, but what are you trying to do?

StartCoroutine returns a Coroutine object… you can yield on that to wait until it completes but they’re not otherwise useful. I suppose you could call StopCoroutine on one too.

It’s generally better to explicitly control your side tasks with delegates or else with some other type of external control logic to make them terminate or to learn if they have terminated.

Remember, coroutines are NOT ASYNCHRONOUS!

Repeat after me: COROUTINES ARE SYNCHRONOUS. There will NEVER be two pieces of user MonoBehavior script code running side by side, EVER.

Here is some timing diagram help:

1 Like

It’s kind of hard to explain, but basically they use it like that to fill a grid, where each coroutine represent one element of that grid. They run all those coroutines at the same time and then wait for all elements to finish, so they can enable other buttons that do filtering on those grid elements.

I understand that coroutines are not running in parallel, I meant non-blocking execution.

Unity documentation about coroutines is really poor immo.

I guess that what they are doing is valid. Thanks for the answer.

That sounds… horrible. just awful. why would you need a bunch of coroutines for that generation process?
If you do it all on application start just let the user wait a second and if you do it on runtime send it to a thread and work on the result.

can you explain what you’re doing more clearly? maybe we can offer a different approach.

Because they are async loading compressed stuff from the asset bundle manager. I guess that they don’t want the app to freeze in one frame.

I’m just the dude who fix bugs for now.

I hear ya, lol.

why are so many coroutines? is each one for a “chunk”?

I feel your pain. You can actually run coroutines yourself in your Update() if you like, if you just need to timeslice up work. All you do is call .MoveNext() on the object returned from the IEnumerator until it returns false… poor man’s coroutine runner. :slight_smile:

That’s all StartCoroutine does, except that it also has ways to not immediately run the next frame, or to run in very specific parts of the Unity update lifecycle, which you may not care about.

I think you may be confusing asynchronous with parallelized. Coroutines aren’t necessarily asynchronous, however that is a primary use case for them. I would say Unity coroutines are asynchronous unless they contain no yield return expressions.

Unity coroutines are not parallelized.

As far as I can tell, StartCoroutine immediately enters the routine and runs until it yields or completes. Thus, I see no difference between

yield return StartCoroutine (RoutineA);

and …

var routine = StartCoroutine (RoutineA);
yield return routine;

In both cases, you are yielding to an already running coroutine.

I think what makes your example interesting is that all coroutines are started and allowed to run asynchronously, but then the loop at the end waits for them synchronously. So the question I have, is “what happens when you yield to an already completed coroutine?”, as that seems a likely possibility. If I were to guess, yielding to a completed coroutine yields a frame, so I’d expect your code to “pause” at least the number of frames equal to the size of the array, even if the coroutines all completed in one frame.

1 Like

I realized that, after nitpicking Kurt, I was pretty careless with my terminology here. I should have said your loop at the end waits for them in serial. It is, in fact, waiting asynchronously. I only see this as an issue because I don’t know what yield returning a completed coroutine does.

I had hoped there would be a way to check if a coroutine was still running, but I can’t see an obvious, built-in solution for that.

Such a construct should work (at least I know for sure that it did in the past). However in the past I discovered that you should not yield on the same coroutine in two different coroutines. So you could not wait for the same coroutine in two seperate coroutines. At least in the past that was messing things up (i.e. one waiting coroutine never resumed).

So yes, you can yield a nested coroutine object just fine if you do it only once. Yielding an already finished coroutine should simply wait for one frame or something like that. Though my test results on that subject are several years old. So feel free to run some simple tests on a more recent version to be on the safe side before you build a complex loading system ^^. Of course if you just do maintenance on an old project, if it worked in the past there’s no reason for it to stop working unless you migrated / updated your project to a newer Unity version (which is generally not recommended when you can avoid it mid development)

Coroutines are very much asyncronous, I think you might be mixing paralism and async programming?

Consider this code

private IEnumerable<int> Foo()
{
   yield return 0;
   Console.WriteLine("One");
}

var foo = Foo() ;
Console.WriteLine("Two") ;

If that code was syncrounous it would have outputed
One
Two

Edit: someone else pointed this out.

This is interesting, and is a good reason to avoid storing a coroutine to a public field or property.

Related to this, I wonder what would happen if you yielded to a coroutine from a nesting coroutine and then yielded to it again from the same, nesting coroutine.

This was my speculation, and a potential issue I see with the OP. As each of these coroutines is used to load a cell in a grid, you could imagine a fairly large number of them. Yielding them serially, with a one frame pause between each, could easily add up to a large, unnecessary delay if all the coroutines complete at about the same time.

There is no reason to start more coroutines from the outer one.

yield return myEnumerator will do it. Though if enumerator is empty it will wait a frame.

You can be very creative, this is from our game

protected IEnumerator Execute(params Func<IEnumerator>[] routines)
        {
            var sequence = new LinkedList<Func<IEnumerator>>(routines);
            var current = sequence.First;

            do
            {
                var routine = current.Value();
                var handPicked = false;
                do
                {
                    if (routine.Current is SequenceState state)
                    {
                        handPicked = true;

                        switch (state)
                        {
                            case SequenceState.Start:
                                current = sequence.First;
                                break;
                            case SequenceState.Previous:
                                current = current.Previous ?? current;
                                break;
                        }

                        break;
                    }

                    yield return routine.Current;
                } while (routine.MoveNext());

                if (!handPicked) current = current.Next;
            } while (current != null);
        }

OP’s post worried about “if it’s safe” so I inferred he was concerned that two coroutines might run simultaneously and interfere in some way. Perhaps I mischaracterized his concern.

I believe a more accurate term that I failed to use was that Unity coroutines are run cooperatively, NOT preemptively.

As in, “only one kid in the swimming pool at a time please.”

No, that’s not true ^^. You assumed that he wants a sequence of coroutines running one after the other. Though that’s not the case. He wants to run them “at the same time” and just wait for all of them to complete. So while we all know that coroutines do not run simultaneously, they do run interleaved. Especially when the coroutine actually waits for a parallel task like a web request which is usually carried out on a seperate thread.

When you execute your coroutines in sequence you would have only one request at a time and it would be much slower. So yes, there are reasons to start multiple coroutines at once. However it’s true that it doesn’t really make a difference if all coroutines are non threaded tasks and just split their work over multiple frames. In this case using multiple coroutines would be just a logical structuring tool without any other benefits.

Yeah then you cant yield the IEnumerator since that will wait until all are done. For example this code in our game

        private IEnumerator WaitForAttachmentHoveringOverRail()
        {
            ShowPopup(railSystem.transform, "Hover over the rail with the attachment.");

            while (attachment.AttachedSystem != railSystem)
            {
                if (attachment.IsCompletelyAttached)
                {
                    yield return Execute<DetachAttachmentStep>(step => { step.Attachment = attachment; step.CorrectAttachmentPlacement = true; });
                    yield return SequenceState.RestartCurrent;
                }
                else
                    yield return null;
            }
        }

Line 9 will not return to Line 10 until Line 9 coroutine is done executing. If you want something simiilar to Task.WhenAny you will need to get creative.

Its true that the only benefit of Coroutines are syntax sugar. They help you code async code in a synchronous manner.

Coroutines really shine for code like this

        private IEnumerator ShowQuitConfirm()
        {
            var result = ShowConfirm("Really quit?");
            yield return result;

            if (result.Result == ConfirmResult.OK)
                Quit();
        }

You will not return from Line 4 until the user have clicked a button in the UI. Imagine doing this without corountines it would be a mess.

Yes, absolutely. That’s what I made the WaitForUIButtons yield instruction for ^^. It was created in a response to this question.

OP you could create your own coroutine runner that runs a collection of IEnumerators. It will be a semicomplex state machine. It need to keep track of each YieldInstructions completed state, though Im not sure we can query a YieldInstructions completed state as we can a CustomYieldInstruction. So you need to create your own versions of WaitForSeconds etc.

Thanks for all the answers, and sorry for the delay. As @Bunny83 pointed out, we want to run them all at the same time because some of those Coroutines have also web service calls. We don’t want to yield each StartCoroutine, that would make our loading system freeze on each object. We want all coroutines quering the backend and their asset bundles at the same time (even knowing that this won’t be in paralell).

Thanks for your complete answer @eisenpony , I guess that is ok for us to waste one frame here and there. I just wanted to make sure that yielding a Coroutine object wasn’t actually calling the coroutine twice.