Skip to content

Day 06 TPL Continued

Kobi Hari edited this page Sep 17, 2020 · 1 revision

Day 06 - Introduction to TPL - Part 2

Projects:

Fun With Tasks Introduction to Tasks and TPL

Task Exceptions

  • We continuted to investigate how exceptions work with tasks.
  • We learned about the AggregateException class that is returned by tasks and understood that the exceptions are always aggragated bacause the task itself is a hierarchic concept
  • We used InnerException and InnerExceptions properties to get the exceptions that were actually thrown in the task
  • We used the Flatten method to convert the exception hierarchy into a flat list
  • We saw that async await catches the first inner exception, not the aggregate exeption, but we can still access the original exception by using the original task and reading from Task.Exception property

Task Cancellation

  • We saw how to use CancellationToken to allow the user to notify the delegate running in another thread that a cancellation is required.
  • We used CancellationTokenSource as means to create a token and to modify its state, so that this privilage is reserved to the caller of the task.
  • We understood that cancallation is a negotiation between the task caller and the task method, where the caller can request cancallation and the method may stop work cancel the task.
  • We understood that the caller cannot force the task to be cancelled - only to request it.
  • We saw that in order for the delegate to cancel the task, in needs to throw OperationCancelledException or to use the token methods to do that.

Progress Reporting

  • We understood that by rules of proper decoupling, the task delegate should not directly update the UI when progress is made
  • We understood also that the task delegate cannot even get an Action<T> instance to report progress, because it does not know (and should not know) in which thread to execute it.
  • We used the IProgress<T> interface to pass an object that can be used to report progess
  • We understood that Progress<T> is a wrapper around Action<T> that captues the synchronization context in which it was created and runs the delegate in that context.
  • We saw how to make the background delegate use the IProgress interface to report progress and how the UI uses it to update the progress bar.

Parallel LINQ

  • We saw how to convert our algorithm to LINQ
  • We then saw how to easily convert it to Parallel LINQ by using the AsParallel method that converts to the IEnmuerable to ParallelQuery
  • We saw how to support cancellation using parallel LINQ method: WithCancellation
  • We did not see it, but we can also report progress as described in the following article from Stack Overflow

Composing Tasks

  • We can get tasks to run in parallel by first creating the task and only then awaiting them.
  • We used Task.WhenAll to combine a collection of tasks into a single task that completes when all the tasks in the collection complete and returns a collection of results.
  • We saw that if some or all of the tasks in the collection throw exceptions, the WhenAll task throws an AggregateException with all the exceptions that were thrown in the original exceptions
  • We used Task.WhenAny to combine a collection of tasks into a single task that completes when one of the tasks in the collection completes.
  • The combined task returns - not the result of the task that completed - but the task itself.
  • We saw that if an exception is thrown in some of the tasks in the collection, the Task.WhenAny does not throw it too. It returns the task that succeeded but does not pass the exceptions that were thrown in the others.
  • In order to catch these exceptions, and make sure that there are no unharvested exceptions, it is recommended to await the other tasks as well.
  • I emphasized again, that it is extremely important to harvest the exceptions from the tasks, even if we do not intend to handle them, becuase when the garbage collector clears a task object, that contains an exception that was not thrown, it throws it and may crash the application.

Tasks that do not run code

  • Tasks created with TaskFactory.StartNew are tasks that report completion of delegates, but not all tasks do that.
  • In fact, it is very important to remember that tasks do not run code at all. They just report completion and result of something... which could be anything, not just delegates
  • We saw that Task.Delay creates a task that does not run code but ends after a specific delay
    • I emphasized that it does not create a new thread and sleeps in that thread, there is not extra thread from this method. It runs nothing, and ends upon timer event
    • We saw that it also accepts cancellation token
  • We saw that Task.FromResult creates a task that is already completed from a known result
  • We saw that we can also create tasks in other statuses, using Task.FromException and Task.FromCancelled
  • We saw how to use Task.FromResult to implement caching

Task Completion Source

  • We saw that Task is a read only object. You can read the status, the result, or the exception, but you can not set them
  • We saw that in order to create a task that we can control, we use an object called TaskCompletionSource
  • We used TaskCompletionSource to create a task that ends when the user presses a button
  • We made sure that the method that generates this task ends instantly, to free the main thread, so that the user may interact with the UI
  • We saw how to also use the CancellationToken and register to the cancellation, so that we cancel the task if the cancellation is requsted.