I have blogged two C# 5.0 articles, both are regarding the new Asynchronous Programming Model (APM); In these articles, I discussed the different APMs that can be used in .NET Framework, I also introduced the new syntactical sugar
await, which simplifies the way to perform asynchronous operations. Many people are aware of the concept of synchronous and asynchronous operations, could somehow distinguish them, but however, there are also a lot of misunderstandings regarding these two, that most of the developers may confuse. In this article, I will clarify what on earth are synchronous and asynchronous operations.
There are many inaccurate explanations to these two concepts that you can find in computer technique sites everywhere on the web. Saying:
- Synchronization means two or more operations are running in a same context (thread) so that one may block another.
- Synchronization means two or more operations happen sequentially.
- Asynchronous means two or more operations are running in different contexts (thread) so that they can run concurrently and do not block each other.
- Asynchronous means two or more operations happen asynchronously.
- And more…
I would like to point out that both of them are inaccurate. See my discussion below.
1. Synchronous Code May or May Not Run in the Same Thread
As all know is that synchronous operations may cause a block, but the root cause leading this block is resource contention, not the operation itself. Let’s say there are four people in a room, and the gate of that room can pass 2 people at a time, to get the four people out of the room, they can pass the gate one by one, or they can be grouped into two, then pass the gate by twice. In the first way, the four people pass the gate in a sequence, if the first person doesn’t get to the gate, the others cannot pass. In other words, if an operation in the sequence is not done, all remaining operations are blocked. The second way, which allows the four people to pass the gate in less time, groups the four people into two, because each time the gate can pass two people, so to quickly pass the gate, we can let two people to pass at a time, in this scenario, each time there are two people passing the gate simultaneously, and we just need to do it twice in a sequence.
Converting this example to the computer words, a set of operations may access one resource, the resource can take several operations at one time, to access the resource, the operations can be performed sequentially, only one operation accesses the resource at one time, and this can be done within a same thread; alternatively, the operations can be grouped by the maximum number of operations that the resource can take, then performs the operations by group, in which the operations are performed simultaneously.
Thinking of a bad case, if the gate is being maintained, to let the people get out of the room, they can either pass one by one, or pass by groups, but whatever options they choose, they cannot get out because now the gate is in a contention, some other operations (maintenance work) are taking on the gate, and other operations (gate passing) are blocked till the contention disappears (maintenance work is done). This causes a block, and the synchronization is required to avoid the contention. So it is obviously nothing to do with whether the operations run on a same thread or multiple threads. Only a contention on specific resource can cause a thread block, and then the synchronization code is desired for.
But what causes a contention on a specific resource? Conceptually, more than one operations manipulate one resource simultaneously with isolated access (which means resource cannot be shared by multiple operations at the same time) causes a contention on that resource.
2. Synchronous Operations Can Perform Sequentially, or Simultaneously
As I just discussed, thread synchronization is required to avoid blocking, a block is caused by a contention, because contention is nothing to do with threads, the synchronous operations can perform in a same thread (sequentially), or in a different thread (possible simultaneously). Here the “synchronous” means “already handled to avoid the thread block”, and doesn’t mean “the operations that can lead a contention”.
So what actually is synchronization? Synchronization is the way to design the thread-safe code where you can totally avoid the resource contention, make sure only one thread is accessing the resource at a time, and lead other threads waiting (blocked) for the resource till the accessing thread releases the resource. Synchronization neither means performing actions at the same time, nor one by one, it just means design the code where it doesn’t cause a resource contention.
3. Asynchronous Operations May or May Not Run in Multithreads
A synchronous operation may be blocked by other operations while they are accessing the same resource, in addition, if the proceeding logic of that synchronous operation doesn’t depend on the particular contented resource, the code will still not run because the current thread that is running this synchronous operation is blocked entirely, causing the other code which is irrelevant to that resource in the running thread to wait till the synchronous operation finishes. This behavior blocks the other threads which attempts to access the contented resource, as well as blocks the continuing code in the thread itself to run. In most of the cases, it may lead the working application to respond slower, or lose the response (this happens if the blocking thread is a UI thread).
As opposed to asynchronous operations, asynchronous operations perform “not-at-a-same-time” and run “somewhere” so that the current running thread is not blocked by it, specifically, when an operation that requires the access to a resource performs asynchronously, it will actually be scheduled to take “at some time later” and run “somewhere”, if the resource may cause a contention, and because that asynchronous operation runs “somewhere”, it will never block the current running thread so that the current running thread can handle other operations. In addition, because the asynchronous operation runs “somewhere”, it will never be affected by the state of the current running thread, if the thread is blocked by a synchronous operation, that asynchronous operation can keep running; if the asynchronous operation finishes with a return value, and meanwhile the running thread (who created the asynchronous operation) is not blocked, the thread can get this return value, and use it in another piece of code.
They are two points here, one is asynchronous operation that runs “somewhere” and is scheduled “at some time”. “Somewhere” often means “another thread”, but you can never say it is always “another thread”. The default implementation of a
System.Threading.SynchronizationContext for Windows Forms application uses a mechanism that we called message loop to implement the asynchronous model in the same thread, which means the asynchronous operation could be run in the same thread who creates it, or in different threads. To get more information regarding this particular case, see Eric Lipper’s blog article: Asynchrony in C# 5.0 part Four: It's not magic.
4. Asynchronous Operations Can be Just Scheduled but Never Guaranteed to Run at a Certain Time
This is another point for the asynchrony: “at some time”. When it starts an asynchronous operation, it will be scheduled to run as soon as possible; in my article, The Asynchronous Programming Models (C# 5.0 Series), I discussed the task-based APM that requires a task scheduler implementation to schedule the asynchronous operations into different tasks, but however you get no way to know when exactly it will run. Hence, there is no prediction or indicator that can let us say these asynchronous operations will run simultaneously, or at different time. Asynchrony means “not-at-a-same-time”, but doesn’t mean “runs in background immediately”.
Please notice that all of the asynchronous operations can be just scheduled, the implementations of the task schedulers may decide where it to run (in same or different threads), when it to run (how the threads are dispatched to run the task as soon as possible) and how it to run (whether if this task will be split into small pieces that can be run in the same thread, same CPU, or multi-core CPUs); Don’t think asynchrony means “other threads”, “background”, “simultaneous” or “concurrent” – actually they are irrelevant.
At last, what is asynchrony? Asynchrony is a way to design the code where you can avoid both resource contention and thread blocking, by putting the operation to a certain place, and scheduling the operation at some time to run. It neither means “running in different threads”, nor “doing things simultaneously”; it's just a way to avoid thread blocking where you can manipulate the resource that may cause a contention.
Now does that make more sense?