Wednesday, August 6, 2008

[2008.08.06] The Task Parallel Library (TPL) [Part II/IV]

Using System.Threading.Tasks.Task

  • in the TPL there are only two abstractions that are being exposed: Task and Parallel (which are replicable tasks) and all other abstractions are built on top of these two
  • the System.Threading.Parallel class is useful for solving common data and task parallel problems
  • it is implemented on top of the lower-level System.Threading.Tasks.Task class, which can be used directly to solve parallel problems with greater flexibility and control over the way work is partitioned
  • Task queues work in a similar manner as the ThreadPool
Example: comparing the similarity in calling the Task and ThreadPool as both code snippets cause the provided delegate to be queued for asynchronous execution – but that is where the similarity ends

// using the ThreadPool

ThreadPool.QueueUserWorkItem(delegate { ... });

 

// using Task

Task.Create(delegate { ... });

  • with the ThreadPool, referencing a queued work item in order to do things like wait on it is a manual and laborious process
  • the Task class makes this operation much simpler
Example:

// [1] to create a task, call the static Create

// method and pass in and Action delegate

// which deals with the work to be done

static Task Create (Action action);

 

// [2] to the get the results of the work, call Wait

void Wait();

NOTE: between the creation point of the task and the point you call Wait(), the action will be potentially executed in parallel
  • the static Create methods return a Task object that represents the newly created asynchronous operation and this instance then provides methods such as Wait
  • so why the executed potentially in parallel and not a guaranteed parallelism?
    • the TPL is for speeding up a lot of work on multiple cores but it is not about fairness or asynchronous programming
    • as a programmer you are exposing potential parallelism in that the computations can be speeded up but the library does not guarantee this parallelism because it would imply a heavier weight mechanism in its design and implementation
Example: want to execute three methods and wait for them to complete

Task t1 = Task.Create(delegate { A(); });

Task t2 = Task.Create(delegate { B(); });

Task t3 = Task.Create(delegate { C(); });

t1.Wait();

t2.Wait();

t3.Wait();

 

// can  replace t1-t2.Wait with WaitAll

Task.WaitAll(t1,t2,t3)

 

// it can be made even simpler using the

//  Parallel.Invoke as it takes an array of actions

Parallel.Invoke(() => A(), () => B(), () => C());

  • unlike the Invoke method, however, using the Task class directly allows for control over how the Task instances are created (using various overloads of Task.Create )
  • it also allows for work to be done between the asynchronous invocations of each of the methods
  • Task also provides cancellation support
  • calling Cancel on a Task may remove the Task from the scheduler’s queue if the Task has not already begun execution
  • for reliability reasons, if the Task has started execution, it will not be aborted
  • instead, its IsCancelled property will be set to return true, which the Task itself can check during execution at periodic intervals
  • the static Task.Current property will return the currently executing Task

No comments: