The task paradigm
Concurrency is primarily about designing algorithms with very loosely coupled units of work, which is often not possible or extends the complexity beyond any possible benefit.
Asynchronous programming is, instead, related to the asynchronous nature of the OS and the devices, whether because they fire events or because it takes time to fulfill the requested operation. Every time the user moves the mouse, types keys on the keyboard, or retrieves some data from the internet, the OS presents data to our process in a separate thread and our code must be ready to consume it.
One of the simplest possible examples is loading a text file from disk and computing the string length, which can be different from the file length, depending on the encoding:
public int ReadLength(string filename) { string content = File.ReadAllText(filename); return content.Length; }
As soon as you invoke this method, the calling thread gets...