We’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-
RunCoroutineAsync. This is a normal pattern when you use
TaskFactory.StartNew with asynchronous delegates (alternatively, you could use
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.