One of the common misconceptions I’ve encountered when developers first start using the Task Parallel Library is that they think Tasks are just fancy threads. This is easy to assume because in a common case, calling Task.Run(…), it actually does run the Task activity on a thread from the thread pool. But as Stephen Toub wrote
The Task-based Async Pattern (TAP) isn’t just about asynchronous operations that you initiate and then asynchronously wait for to complete. More generally, tasks can be used to represent all sorts of happenings, enabling you to await for any matter of condition to occur.
For example, let’s say I have a WPF app with two buttons, Start and End. The Start button’s event handler looks like this
which uses an async method to retrieve the current time. But we can implement GetCurrentTimeAsync() this like the following
The Task returned from GetCurrentTimeAsync() will never complete until the user clicks the End button. In this case, the Task activity isn't code, but the act of waiting for the user to click the End button. In real code the activity could be happening on the network, the memory bus of the machine or anywhere, but it isn't necessarily on a thread.
In fact, Lucien Wischik has an excellent series on turning all sorts of things into Tasks so that they can be awaited.