Some things take a long time to finish: downloading a 400MB file, processing the individual bits of a huge bitmap.
Why a Task
and not a Thread
for asynchronous ops: a Thread can't return a result (it can write to some shared memory), can't be cancelled, doesn't play nice with other threads by nature (always races), has a lot of overhead (a lot).
- Cpu-bound vs. I/O bound
- Use existing framework/library sources of asynchrony or create your own with Task.Run`
- Long-running tasks: thread depletion in the ThreadPool.
- So what is up with
async void
methods and error handling? - How does this impact event handlers?
- Synchronous vs. asynchronous throwing
- Awaiting faulted tasks
- Tasks that don't lean on an asynchrony source are executed synchronously.
- Not awaiting tasks (that only produce results or trigger side effects) is bad because there is no information about completion of the query/command, so you can't schedule more stuff to happen afterwards.
async void
is a killer of asyncrony (not necessarily a bad thing). If yourTask
/Task<T>
does actually wrap an asynchrony source (of which there are only a few:Task.Run
(a.k.a theThreadPool
), any async I/O implementation from the framework (likeFileStream.ReadAsync
), aTaskCompletionSource<T>
that wraps a new thread (STA or otherwise)... and that's about all I can come up with), then having it awaited in anasync void
function will not register a continuation (no new Task will be created to be awaited), and thus all statements that follow theasync void
method invocation will be executed immediately after said invocation (as opposed to being captured in a continuation).
The following functionalities, captured in the UsagePatterns project are all taken and extended from Liam Westley's brilliant NDC London 2014 talk Async C# 5.0 - Patterns For Real World Use so props to him for a great presentation and source material, check out his original implementation. The demo album of the featured band, Silents, must be downloaded in all formats (m4a, flac, mp3, ogg) and placed in the root repo folder inside a folder called media, or the path to the location of the download needs to be adjusted in the MediaService implementation.
Also note that clearing the Windows file cache is essential for running this project under Windows multiple times (for now at least). You could run the solution / exe as an admin and make use of the Empty Standby List tool by placing it in the Utility folder for the task to be automated.
There is one built-in progress reporter:
Progress<T>
. You can either pass anAction<T>
into the constructor or handle theProgressChanged
event. One important aspect of this class is that it invokesProgressChanged
(and theAction<T>
) in the context in which it was constructed. So it’s natural to write UI updates.
Stephen Cleary - Reporting Progress from Async Tasks
- You can report progress for a
Task.WhenAll
task, by injecting individualIProgress<T>
in each task and have them use the same reporting method. - Since the reporting is done on the same thread (the UI thread that constructs the
Progress
objects in our case), there is no need for synchronization, since all calls come in sequentially and are handled synchronously. - Each individual task will report incremental updates (how many bytes they managed to read in one pass) and we will cumulate everything in the common reporting action (average the cumulated bytes read by the number of tasks).
- We introduce the concept of a (sequential) bound command since, for example, the cancellation cannot happen unless downloading is in progress.
- Witness the proper cancellation etiquette that involves throwing an
OperationCanceledException
. - Catching said exception type for handling the cleanup phase at a task level and also at the central point of aggregation (the common reporting action).
- Use a fixed size buffer for running a set number of tasks at a time.
- Show the progress by assigning each running task one specific bar to constantly update with new info.
- Allow for cancellation
Remove duplication in view models by introducing a base class
- Cancel all running tasks when the first one is ready
- Cancel all running tasks if a validation on one's result fails