A long time ago, asynchronous programming was an exotic practice. Not many people were doing it, and their code was punctuated with things like assembly language and processor interrupts. Less anciently, preemptive multitasking OS’s made asynchronous programming more accessible, albeit often still with arcane and unnatural boilerplate, not to mention hazards of sharing mutable data.
Today asynchronous programming is mainstream — more than that, it’s a firm expectation of polished software for desktop, mobile, and web. The tools for asynchronous programming have gotten better, even as far as adding syntactic sugar, which
An Aside to Purists
Just to get it out of the way, let me go ahead and admit: this article is hopelessly procedural-language-normative, or at least popular-language-normative. I recognize that the purest approaches to asynchronous coding have been laid out by our bearded forebears in MIT labs in LISP and kindred languages based on immutable data; and that if everybody used Erlang the world wouldn’t have so many problems, and…
What are Coroutines?
Coroutines play ping pong with each other in the order of executing their code. In other words, Code A executes until it reaches a deferral instruction, then it yields execution to Code B which executes until it reaches a deferral instruction, then yields back to Code A, and so forth. The difference between coroutines versus two unrelated parallel threads of execution is that with coroutines, control is explicitly passed back and forth, so that the coroutine author can allow one routine to handle outputs of the other.
According to a quote attributed to Don Knuth, any subroutine is a special case of a coroutine, specifically a coroutine that only performs one exchange with its calling process instead of many.
Building On Enumerators
A simple kind of coroutine is an iterator or generator. Here’s a Python example:
The above code is a coroutine, because it “ping pongs” back and forth between the outer
routine and the inner
routine. The inner function’s stack and state are preserved every time it
s control to the outer caller, and if enumerated further, it seamlessly picks up its old state to enumerate or generate further.
However, the example above is synchronous, not asynchronous, as it will block execution while the inner function searches for valid return values. That’s not very interesting. How about an asynchronous example?
A Truly Asynchronous Coroutine
Compare the following truly asynchronous code written in C#:
This time whoever calls
will immediately get back a
instance, and the knowledge that somewhere, somehow, the invoked code may be still executing asynchronously. Thus the caller won’t be blocked, and can use that
instance to interact with the coroutine. Perhaps the caller would
that task, just as
Less Generator, More Worker
Notice also that the C# version above is not written like the Python enumerator. While I guess we could have our routine enumerate a type of IEnumerable<Task>, that is not a very common or useful way to think about this, since the heavyweight multithreading we are employing suggests a best practice of offloading maximized chunks of work, not engaging with chatty enumerators.
In the real world, the most common reason for using
is to spare the UI thread from blocking. You could extend that to any other thread and say that the purpose is to easily interact with coroutines that run on other threads, while ensuring you always resume on your original thread.
This is particularly useful in mobile development where it is critically important to interact with the UI only on the UI thread, while delegating disk or network IO to a secondary worker thread.
Mitigating Race Conditions
One of the banes of multithreaded or asynchronous programming over the years has been shared state between threads, and the need to protect that state from corruption due to race conditions. Presumably the best way to solve that problem is to ditch our whole ecosystem and go with an immutable message passing scheme. That certainly excludes common categories of async bugs, at the cost of coding in a fringe language. However as mentioned above, we can’t go there for this article, so let’s look at best practices for mitigation with the tools we have.
One of the benefits of C#’s
is that you are guaranteed that the
function always runs on the same thread, unless it happens to manually spin up a worker. When the
function awaits another
function, it may be that the other function spins up a worker thread. However when control returns to us after an
, it will always be on our original thread.
Besides the obvious benefit of being able to trivially inspect mobile code to be sure it is running on the UI or main thread, this also can help with state integrity. As long as all the hard and time consuming calculations are being done on the worker thread, and the worker thread returns its results to us, we can just take the results of the worker and update the app’s shared state in the outer
routine. That way we don’t need to worry about state being corrupted due to two or more threads directly accessing it.
Doing It Wrong
Image credit: thewebprincess on Flickr under CC
Powerful tools are often dangerous for the same reasons they are powerful. You can use asynchronous programming to shoot yourself in the foot if you don’t stay on well trod paths. For example, with C#’s
, as long as you just use the keywords
and perhaps an occasional
for branching asynchronicity, you will be fine.
The temptation of a beginner is to invoke those methods of a
/coroutine that block, such as the
property or the
method. There are reasons those methods exist, but 99% of the time a newbie invokes them, the methods will deadlock due to the way the thread pooling works. Newbies are well advised to only use
and maybe occasionally
, but never to touch the blocking stuff.
Earlier I referred to
as syntactic sugar, which it is. As such, its chief benefit is reducing the levels of indentation for complex async invocations, and simplifying the exception handling. After getting used to
, you start to feel sorry for people who have to write asynchronous code without it — sometimes including oneself depending on one’s current project.
Async code written “the old way” suffers from wordiness, and lends itself to a toxic compounding of complexity in indentation and error handling logic. Often it just turns into spaghetti, whereas
can reverse that trend quite elegantly.
Language Survey: C#
For better or worse, C# is the standard bearer with
. The merits of its particular flavor of asynchronous coroutines may be debated as all programming languages are, however C# has certainly affected design choices in newer languages and language revisions.
Language Survey: C++
Many developers perceive that C++ has undergone a renaissance of late, seeing inclusion of such traditionally high level features as lambdas, generics, and
type inference. Microsoft has experimented in their Code Technology Preview (CTP) releases with
for Visual C++, and more recently a
The official C++ language change to support coroutines has been deferred and didn’t make it in time for C++17. Here’s a link to what it may eventually look like, using new keywords of
An example from Microsoft using the latest proposal:
Here’s a cool example of what it could look like from Eddie Zaneski’s blog linked above:
Language Survey: Python
Async/await has already seen adoption in Python 3.5 via PEP 492. It is self consciously a product both of its roots in Pythonic generators and also of the C# pattern.
is truly asynchronous, it requires a message loop. Here’s an example from the PEP:
Language Survey: Swift
Objective C is pretty much a language that only its mother could love, and accordingly Apple’s swank replacement has been greeted with much fanfare and enthusiasm in the iOS and Mac development community. Unfortunately or fortunately depending on your point of view, Apple has been slow and cautious about rolling out new Swift language feaures. Swift supports Apple’s Grand Central Dispatch for asynchronous programming, but not yet any syntactic sugar like async/await.
That hasn’t stopped Swift enthusiasts from writing their own async/await- like features, however. Early attempts were limited to following the form of C# async/await while actually blocking execution, which misses a major point of the exercise. Later research has been promising, albeit somewhat patched together and bleeding edge. It is likely that Apple would need to create an elegant final solution, though this may be complicated by Apple’s architectural choices inherited from Obj-C and implicit to the standard Apple frameworks.
An example from the above link:
This has been an informal review of C#’s particular flavor of asynchronous coroutines using the
keywords, and how that construct has infected(?) other programming languages where it seems to be evolving in parallel. I welcome a future where asynchronous programming is made ever easier and where the common use cases are easy to get right and hard to get wrong. End users will appreciate the smoother experience, too.
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.