text: Too Many Coroutines: A Queue Solution
  Unity’s coroutine support is great. So great that it’s easy to go overboard and end up with too many of them. That could be for any number of reasons. Perhaps the coroutines are using too much memory or have too many files open at once. In any case, you’ll need to find a way to limit how many are running at a single time. Today’s article introduces a solution to the problem that queues coroutines so you never have too many running. Read on to learn about the solution and for the class that implements it.
  The following queue solution is simple in concept but has some tricky elements that are vital to make it work. All we’re going to do is run coroutines until there are too many running, queue any excess coroutines, and run them when the first coroutines finish. Sounds easy!
  The biggest issue with this approach is this bit: “when the first coroutines finish”. How do you know when a coroutine has finished? Do you get a callback? Is an event dispatched? No, you don’t get notified by Unity in any of these ways. The normal approach is to put your “coroutine finished” code in the coroutine itself like this:
  IEnumerator DownloadText(string url, Action<string> callback) { // Do the coroutine's work var www = new WWW(url); yield return www; // Notify that the coroutine is finished callback(); }
  This works great for specific coroutines, but the coroutine queueing system should work for any coroutine. It can’t know about the specific function signatures of coroutines like
  DownloadText, so we need another way to find out if the coroutine is done.
  A better way is for the queueing system to run its own coroutine instead of the one the user wants it to run. This way it can be notified when the coroutine is finished. Something like this:
  IEnumerator QueueSystemCoroutine(IEnumerator userCoroutine, Action callback) { // ... Run the user's coroutine (e.g. DownloadText) // Notify that the coroutine is finished callback(); }
  So how do we run the user’s coroutine? Luckily, coroutines are just an
  IEnumerator so it’s easy to loop over them and
  yield return their values:
  IEnumerator QueueSystemCoroutine(IEnumerator userCoroutine, Action callback) { // Run the user's coroutine (e.g. DownloadText) while (userCoroutine.MoveNext()) { yield return userCoroutine.Current; } // Notify that the coroutine is finished callback(); }
  Each time we call
  MoveNext the user’s coroutine is resumed and a
  bool is returned to indicate if it yielded anything. If it did, we can get it from the
  Current property. This means that the
  while loop basically just runs the user’s coroutine and yields all its values.
  Now that we’ve solved the tricky part, let’s take a look at the class that implements the coroutine queue system:
  using System; using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// Imposes a limit on the maximum number of coroutines that can be running at any given time. Runs /// coroutines until the limit is reached and then begins queueing coroutines instead. When /// coroutines finish, queued coroutines are run. /// </summary> /// <author>Jackson Dunstan,</author> public class CoroutineQueue { /// <summary> /// Maximum number of coroutines to run at once /// </summary> private readonly uint maxActive; /// <summary> /// Delegate to start coroutines with /// </summary> private readonly Func<IEnumerator,Coroutine> coroutineStarter; /// <summary> /// Queue of coroutines waiting to start /// </summary> private readonly Queue<IEnumerator> queue; /// <summary> /// Number of currently active coroutines /// </summary> private uint numActive; /// <summary> /// Create the queue, initially with no coroutines /// </summary> /// <param name="maxActive"> /// Maximum number of coroutines to run at once. This must be at least one. /// </param> /// <param name="coroutineStarter"> /// Delegate to start coroutines with. Normally you'd pass /// <see cref="MonoBehaviour.StartCoroutine"/> for this. /// </param> /// <exception cref="ArgumentException"> /// If maxActive is zero. /// </exception> public CoroutineQueue(uint maxActive, Func<IEnumerator,Coroutine> coroutineStarter) { if (maxActive == 0) { throw new ArgumentException("Must be at least one", "maxActive"); } this.maxActive = maxActive; this.coroutineStarter = coroutineStarter; queue = new Queue<IEnumerator>(); } /// <summary> /// If the number of active coroutines is under the limit specified in the constructor, run the /// given coroutine. Otherwise, queue it to be run when other coroutines finish. /// </summary> /// <param name="coroutine">Coroutine to run or queue</param> public void Run(IEnumerator coroutine) { if (numActive < maxActive) { var runner = CoroutineRunner(coroutine); coroutineStarter(runner); } else { queue.Enqueue(coroutine); } } /// <summary> /// Runs a coroutine then runs the next queued coroutine (via <see cref="Run"/>) if available. /// Increments <see cref="numActive"/> before running the coroutine and decrements it after. /// </summary> /// <returns>Values yielded by the given coroutine</returns> /// <param name="coroutine">Coroutine to run</param> private IEnumerator CoroutineRunner(IEnumerator coroutine) { numActive++; while (coroutine.MoveNext()) { yield return coroutine.Current; } numActive--; if (queue.Count > 0) { var next = queue.Dequeue(); Run(next); } } }
  You’ll recognize
  QueueSystemCoroutine as
  CoroutineRunner, albeit with some of the queue system’s work built in. Now let’s see a little
  MonoBehaviour that uses
  CoroutineQueue:
  public class TestScript : MonoBehaviour { void Start() { // Create a coroutine queue that can run up to two coroutines at once var queue = new CoroutineQueue(2, StartCoroutine); // Try to run five coroutines for (var i = 0; i < 5; ++i) { queue.Run(TestCoroutine(i, 3)); } } // A coroutine that logs its lifecycle and yields a given number of times IEnumerator TestCoroutine(int id, uint numYields) { Debug.Log("frame " + Time.frameCount + ": start " + id); for (var i = 0u; i < numYields; ++i) { Debug.Log("frame " + Time.frameCount + ": yield " + id); yield return null; } Debug.Log("frame " + Time.frameCount + ": end " + id); } }
  The test prints out this log (with annotations by me):
  // starts the first coroutine (0) frame 1: start 0 frame 1: yield 0 // starts the second coroutine (1) frame 1: start 1 frame 1: yield 1 // next frame, the two running coroutines (0, 1) yield frame 2: yield 0 frame 2: yield 1 // next frame, the two running coroutines (0, 1) yield again frame 3: yield 0 frame 3: yield 1 // next frame, the first coroutine (0) ends frame 4: end 0 // the first queued coroutine (2) starts frame 4: start 2 frame 4: yield 2 // the second coroutine (1) ends frame 4: end 1 // the second queued coroutine (3) starts frame 4: start 3 frame 4: yield 3 // next frame, the two running coroutines (2, 3) yield frame 5: yield 2 frame 5: yield 3 // next frame, the two running coroutines (2, 3) yield again frame 6: yield 2 frame 6: yield 3 // next frame, the third coroutine (3) ends frame 7: end 2 // the last queued coroutine (4) starts frame 7: start 4 frame 7: yield 4 // the fourth coroutine (3) ends frame 7: end 3 // next frame, the only remaining coroutine (4) yields frame 8: yield 4 // next frame, the only remaining coroutine (4) yields frame 9: yield 4 // next frame, the only remaining coroutine (4) ends frame 10: end 4
  You can see that the queue is accepting arbitrary coroutines, capping the number of them that run at once, detecting when they finish, and running queued coroutines until there are no more to run. So we have a working solution. Lots of embellishments could be made to it, such as allowing for the cap to change, dispatching events when coroutines finish or the queue is empty, making it implement an interface, and much more. Feel free to take the code and add the features you need. If you do, please post about it in the comments.
  Do you use coroutines or C# iterators in your Unity projects? Did you ever need to limit how many were running? Post about your experiences in the comments!
  #1 by Yuri Tikhomirov on November 18th, 2015 · | Quote
  Hey, nice job!
  Shouldn’t we call Dispose() on the ‘coroutine enumerator’ after all?..
  #2 by jackson on November 18th, 2015 · | Quote
  Thanks! :)
  There’s an interesting subtlety to
  IEnumerator: it doesn’t provide a
  Disposemethod as you can see from the documentation. However, its sibling interface
  IEnumerator<T>does have a
  Disposemethod, as mentioned in the documentation.
  This means that it’s not possible for
  CoroutineQueueto call
  Disposewithout casting or reflection, so it probably shouldn’t. An alternative would be for
  CoroutineQueueto also take
  IEnumerator<T>instances. I think that’d be a bit strange though since Unity’s coroutines are all built around the non-generic
  IEnumerator. If it were an
  IteratorQueuethen that might make more sense.
  For more about
  IEnumeratorand
  IEnumerator<T>, including some discussion of their
  Dispose, check out my article Do Foreach Loops Create Garbage?
  #3 by Cesar on July 17th, 2016 · | Quote
  You saved me, really great work!
  #4 by onesoftgames on December 14th, 2016 · | Quote
  Hi @jackson,
  Your articles are all awesome! This CoroutineQueue class is exactly what i need.
  I found a small bug if we try to call Coroutine without parameter, like this:
  In log console, we can see “end TestCoroutine” only print one. Maybe without params, unity treat 2 instance of our coroutine as one? In this case, is there any solution?
  (If we call TestCoroutine with params e.x id , it is print two times as we expected.)
  #5 by jackson on December 15th, 2016 · | Quote
  Glad you’re enjoying the articles! Let me know if you have any ideas for articles you’d like to see. I’m always looking for topics that readers will appreciate. :)
  I copied your code into Unity 5.5.0f3 and ran it with the same
  CoroutineQueuefrom the article and it printed all the logs for both coroutines, including the “end TestCoroutine” ones. Any tips on how to reproduce your issue?
  #6 by onesoftgames on December 18th, 2016 · | Quote
  Opp! I am sorry, it is my mistake.
  In Console View, the Collapse feature is turn on. That why they group two message into one and show small “2” number on the right.
  I just start working with unity so I can not see it from start :p
  Thank so much!
  #7 by onesoftgames on December 19th, 2016 · | Quote
  And about ideas for articles, It will be great if you can compare and bring the best solution for
  “Event System – Dispatcher” System. Event and message dispatching is a critical part of any game!
  Thank so much!
  #8 by jackson on December 19th, 2016 · | Quote
  Thanks for the idea! I’ve done a few articles about event and callback performance:
  Event Performance: C# vs. UnityEvent
  Fastest Callbacks: Delegate, Event, or Interface?
  Unity Function Performance Followup
  Was there something specific you’d like me to cover that I haven’t so far?
  #9 by onesoftgames on December 20th, 2016 · | Quote
  Thanks! I learn a lot from these articles.
  For the ideas, I mean something like this:
  Can you create a simple but very fast event system?
  #10 by jackson on December 20th, 2016 · | Quote
  Thanks for the link. I haven’t used that library before, but I can guess how it works based on the documentation. The
  MessageDispatcher.AddListenerfunction takes a delegate that they say looks like this:
  Since delegates and C# events are very similar, you can expect the performance to be no better than delegates or events in any of those articles I linked. It should be a ton faster than
  GameObject.SendMessageunless the string filtering or time delay features impose some huge overhead, but probably not.
  As an aside, I’d personally prefer to not use strings for the event name and to strongly type my event parameters. If you prefer that too, you can get most of the benefits of that library by simply declaring static events:
  Then use it similarly to their example:
  Thanks for the article idea. I may very well write an article about higher-level event systems than just events and delegates.
  #11 by onesoftgames on December 20th, 2016 · | Quote
  Thank you so much! I can’t wait to see your next articles!
  #12 by Tristan on November 8th, 2018 · | Quote
  This is very useful. Thank you for sharing.
  #13 by MM on April 17th, 2019 · | Quote
  I needed to implement a generic coroutine queue and this fits the bill. Thank you so much
  #14 by Arch on May 15th, 2019 · | Quote
  Hi, I used your solution for a long time now, I just noticed that the recursive calling can bring to very large overhead when queuing hundreds of coroutines and loging in them.
  Would you have an elegant non recursive solution ?
  I added a while(true) corroutine in my solution but I find it quite clumsy.
  Thanks
  #15 by jackson on May 16th, 2019 · | Quote
  Which “recursive calling” are you referring to?
  #16 by christian on October 29th, 2019 · | Quote
  This looks very interesting.
  Is there any way to pause/stop the queue?
  Is there any way to insert a job at the top of the queue?
  #17 by jackson on October 31st, 2019 · | Quote
  These features aren’t supported by the code in the article, but you could certainly modify the code to implement these pretty easily.