Async Coroutines
• CommentsWe’ve been introduced to scheduled concurrency. Now how about a quick example?
Today we’re going to use the exclusive scheduler to create a simplistic kind of round-robin co-routine, similar to Jon Skeet’s EduAsync Coroutines.
Please note: this is only “playing around” code. Do not use this in production!
There isn’t that much to it. We define three co-routines with slightly different behavior to make it a little interesting: FirstCoroutine
yields twice, SecondCoroutine
yields three times, and ThirdCoroutine
yields once.
To run the co-routines exclusively, we create a TaskFactory
wrapping a ConcurrentExclusiveSchedulerPair.ExclusiveScheduler
. We also create a convenience method RunCoroutineAsync
, which takes a co-routine delegate and executes it on that scheduler.
The tricky part in this code is the double-await
in RunCoroutineAsync
. This is a normal pattern when you use TaskFactory.StartNew
with asynchronous delegates (alternatively, you could use Task.Unwrap
).
Logically, the “coroutine” parameter to RunCoroutineAsync
is an asynchronous delegate (referring to one of the async co-routine methods). When we pass it to StartNew
, we get back a Task<Task>
representing the starting of that asynchronous delegate on our exclusive scheduler. The inner task represents the completion of that asynchronous delegate. So the await await
is used because we want RunCoroutineAsync
to complete only when the asynchronous delegate completes.
If we execute this program, we can clearly see the co-routine behavior:
Starting FirstCoroutine
Yielding from FirstCoroutine...
Starting SecondCoroutine
Yielding from SecondCoroutine...
Starting ThirdCoroutine
Yielding from ThirdCoroutine...
Returned to FirstCoroutine
Yielding from FirstCoroutine again...
Returned to SecondCoroutine
Yielding from SecondCoroutine again...
Returned to ThirdCoroutine
Finished ThirdCoroutine
Returned to FirstCoroutine again
Finished FirstCoroutine
Returned to SecondCoroutine
Yielding from SecondCoroutine again...
Returned to SecondCoroutine again
Finished SecondCoroutine
Just one final word. There are benign race conditions in this code: e.g., it’s possible that FirstCoroutine
may run and yield to itself before SecondCoroutine
even starts. The ExclusiveScheduler
does not make guarantees about queueing or fairness (though it does try to be fair) - it only guarantees exclusive scheduling.