Concurrency is a big topic. In the most zoomed-out view, concurrent software has multiple "active" components — sometimes called processes or tasks — that can be in-progress simultaneously. The simultaneity can be physical (different tasks executing on different processors) or simulated by interleaving the execution of multiple tasks on a single processor. This page is my take on some of the most important concepts that fit under the concurrency umbrella.
The reasons for making concurrent software can be lumped into two main categories: performance and expressiveness.
- Performance - There are multiple hardware resources of some sort (processors, network interfaces, machines, ...) and we want our software to use them simultaneously to improve some performance metric (total run time, throughput, latency, whatever).
- Expressiveness - Some program patterns are intrisically concurrent (interactive programming is concurrenct programming), and some programs can be more elegantly expressed by using the right concurrency framework.
Of course a single application might use concurrency in multiple ways for different reasons, but I think it's important to keep in mind that these are two categories are quite distinct. The Charcoal project is principally focused on the expressiveness part of the picture.
Expressiveness is something of a catch-all in the sense that we mean "all the reasons for using concurrency that aren't directly related to performance". We put three primary concepts within the scope of expressiveness:
All software interacts with the real world in some way. Some applications have a relatively simple interactions pattern: read some input, compute for a while, write some output. Compilers are a good example of software that is quite complex, but mostly fits this simple interaction pattern.
Many applications, including most that people directly use on a regular basis, have more complex interactions with the world. Take a spreadsheet as an example. The application must be ready to react to keyboard and mouse input from the user at any time. Recalculating the spreadsheet values may take a relatively long time for large documents, and this process should be interruptable. Also various background tasks are probably happening periodically, like auto-saving backup copies and checking formulas for possible errors.
Building reactive/interactive software is different.
Some algorithms look "procedural/functional" from the outside, but can still use concurrency to good effect internally. A classic example is programming language parsing. Scanner; parser.
Sometimes we want software components to be isolated from each other in one way or another. A couple of classic examples:
- Plugins. Many applications have plugins. Often we want the core application to be isolated from the plugins, in case the plugin does something bad.
- Long-running Tasks. Often in GUI applications, many tasks can be taken care of very quickly, but a few might run for a long time. We need to isolate the long-running tasks in some way to ensure that the application remains responsive.
Here we have a grab-bag of concepts that are either combinations of the ideas discussed above or more specitic instances of them. They are kinda sorta in order starting with the most relevant to Charcoal.
One common and simple concurrency pattern in GUI applications is starting long-running I/O operations, like disk or network reads, while remaining responsive to user input.
This category is also connected to the concurrency part of the map, because some software has high disk or network requirements and needs to overlap many accesses in order to perform well.
A special case of interactive software is those that have real-time constraints (hard or soft). This category includes multimedia software, games, embedded systems controllers.