Parallel Programming : Task Relationships (part 2) - Parent and Child Tasks

11/15/2011 9:05:33 AM

2. Parent and Child Tasks

So far, you haven’t seen examples that create subtasks. A subtask is a task created in the context of another task. The outer task is not necessarily a parent task. There is no implied relationship. The default task scheduler (the .NET Framework 4 thread pool) handles subtasks differently than other tasks. Work stealing, as explained in the next section, can occur whenever subtasks are created.

In the following exercise, you will create a task and subtask.

Create an outer task and a subtask

  1. Create a console application. In the Main function, create a new task by using the Task constructor. Initialize the task with a lambda expression. This will be the outer task in a task relationship.

    var outer = new Task(() => {

  2. In the lambda expression, display the name of the current task.

    Console.WriteLine("Outer task.");

  3. Still within the lambda expression, create and start a task. Because you are within an existing task, this is a subtask. In the lambda expression for the new task, display the name of the task.

    var inner=Task.Factory.StartNew(() => Console.WriteLine("Inner task."));

  4. Wait for the subtask and close the lambda expression for the outer task.

    inner.Wait(); });

  5. To complete the example, start and then wait for the outer task. As part of the outer task, the inner task will also be started.

    class Program
    static void Main(string[] args)
    Task outer = new Task(() =>
    Console.WriteLine("Outer task.");
    var inner=Task.Factory.StartNew(() => Console.WriteLine(
    "Inner task."));

You can convert a subtask relationship into a parent/child relationship. You might want to do this for a variety of reasons, including creating a hierarchy of tasks. Instantiating a subtask does not immediately confer a parent and child relationship between the two tasks. In addition to a subtask, you must also choose the TaskCreationOptions.AttachedToParent option to indicate a parent/child relationship.

The TaskCreationOptions.AttachedToParent method binds the lifetime of the parent and child tasks. In other words, when waiting for the parent task, you are essentially waiting for both the parent and the child task to complete. The parent might complete before the child. If that occurs, the status of the parent task becomes TaskStatus.WaitingForChildrenToComplete. When the child task eventually finishes, the status of the parent task is updated appropriately, for example, to TaskStatus.Completed or TaskStatus.Faulted.

Create a parent task and a child task where the duration of the child task is longer than the parent. Report the status when the parent task completes execution.

  1. Create a console application. Implement a computer-bound method.

    static void DoSomething() { Thread.SpinWait(4000); }

  2. In the Main function, create a new task by using the Task constructor. Initialize the task with a lambda expression. This will be the parent task.

    var parent = new Task(() => {

  3. In the lambda expression for the parent task, display the name of the current task.

    Console.WriteLine("Parent task.");

  4. Still in the lambda expression, create and start a child task by using the TaskFactory.StartNew method. In the lambda expression for the child task, sleep for 5,000 milliseconds by using the Thread.Sleep method. Be sure to define a parent/child relationship with the TaskCreationOptions.AttachedToParent option.

    Task.Factory.StartNew(() => { Thread.Sleep(5000); },

  5. Start the parent running with the Task.Start method. The child task is executed as part of the parent task.


  6. Wait for the parent to complete. If the wait operation times out, query whether the parent is waiting for child tasks to complete. If the parent task is waiting for the child, display the status of the parent task.

    class Program
    static void Main(string[] args)
    Task parent = new Task(() =>
    Console.WriteLine("Parent task.");
    Task.Factory.StartNew(() => { Thread.Sleep(5000); },
    if ((!(parent.Wait(2000)) &&
    (parent.Status == TaskStatus.WaitingForChildrenToComplete)))
    Console.WriteLine("Parent completed but child not finished.");

3. The Work-Stealing Queue

Historically, thread pools have a single global queue in which to place work items. The thread pool dequeues work items from the global queue to provide work to available threads. The thread pool exists in a multi-threaded environment, so the global queue must be thread safe. The resulting synchronization can adversely affect the performance of the thread pool and indeed the overall application.

Because a single global queue is a potential bottleneck, the .NET Framework 4 thread pool offers a global queue and any number of local queues. This scheme allows work items to be distributed across several queues and removes a single point of synchronization. Parent tasks can be scheduled on the global queue, while subtasks are placed on local queues.

Work items in the global queue are accessed in a first-in, first-out (FIFO) manner, whereas local queues are last-in, first-out (LIFO). In addition, local queues are double-ended; they have a private side and a public side. The private side is virtually lock free and is accessible only from the current thread. Other threads access the queue from the public side, which is controlled using synchronization. This explanation is somewhat of a generalization, but hopefully it is sufficient to convey the essence of work stealing.

Ultimately, work stealing is a performance optimization. A subtask is placed on a local queue and then scheduled (FIFO) to run on an available thread in the thread pool. After the task completes, the now-available thread returns to the same local queue for additional work. When this queue is empty, the thread can freelance and help other local queues with pending work. If work is found in another local queue, a task is dequeued (LIFO) and run on the available thread. This process is called work stealing. Here is a typical scenario:

  1. A primary task is started and placed on the global queue.

  2. When the primary task runs, it creates a subtask. The new task is then placed on a local queue.

  3. The subtask completes. The thread pool searches for additional work to give to the newly available thread, which it does as follows:

    1. Search the same local queue for another task to dequeue (LIFO) and execute.

    2. If the local queue is empty, find work for the thread on another local queue (LIFO).

    3. If a task is found on another local queue, it is dequeued (FIFO) and executed. In essence, the thread just “stole” work from another local queue. However, in this context, stealing is helpful.

Instead of stalling a thread, work stealing keeps a thread busy even when its local queue is empty. The stolen task is taken from the back of another local queue. This must be synchronized for thread safety, because there might be other work-stealing threads that need work at the same time. However, that synchronization is an infrequent penalty, because most tasks are taken from the private front end of the local queue.

Subtasks of long-running threads are not placed on a local queue. Long-running tasks are scheduled on a dedicated thread and not on a thread in the thread pool. Similarly, subtasks of long-running tasks are scheduled on a dedicated thread as well. Therefore, long-running tasks exist outside of the thread pool and do not benefit from work stealing.

Here is a diagram of the work-stealing process.

  •  BizTalk 2006 : Handling Ordered Delivery
  •  BizTalk 2006 : Implementing Dynamic Parallel Orchestrations
  •  Windows System Programming : The Registry
  •  Windows System Programming : File Locking
  •  SharePoint 2010 : Security - Secure Store Service & Using SSS with BCS
  •  SharePoint 2010 : Security - Claims Based Authentication
  •  SharePoint 2010 : PerformancePoint Services (part 2) - Using PerformancePoint
  •  SharePoint 2010 : PerformancePoint Services (part 1) - PerformancePoint Central Administration Settings
  •  Windows System Programming : Example: Listing File Attributes & Setting File Times
  •  Windows System Programming : File Attributes and Directory Processing
  •  Windows System Programming : File Pointers & Getting the File Size
  •  SharePoint 2010 : Business Intelligence - Excel Services (part 2) - Accessing Excel Services Over SOAP
  •  SharePoint 2010 : Business Intelligence - Excel Services (part 1) - Accessing Excel Services Over REST
  •  SharePoint 2010 : Business Intelligence - Visio Services
  •  Exchange Server 2010 : Perform Essential Database Management (part 3) - Manage Database Settings
  •  Exchange Server 2010 : Perform Essential Database Management (part 2) - Manage the Transaction Log Files
  •  Exchange Server 2010 : Perform Essential Database Management (part 1) - Manage the Database Files
  •  Architecting Applications for the Enterprise : UML Diagrams (part 3) - Sequence Diagrams
  •  Architecting Applications for the Enterprise : UML Diagrams (part 2) - Class Diagrams
  •  Architecting Applications for the Enterprise : UML Diagrams (part 1) - Use-Case Diagrams
    Top 10
    Nikon 1 J2 With Stylish Design And Dependable Image And Video Quality
    Canon Powershot D20 - Super-Durable Waterproof Camera
    Fujifilm Finepix F800EXR – Another Excellent EXR
    Sony NEX-6 – The Best Compact Camera
    Teufel Cubycon 2 – An Excellent All-In-One For Films
    Dell S2740L - A Beautifully Crafted 27-inch IPS Monitor
    Philips 55PFL6007T With Fantastic Picture Quality
    Philips Gioco 278G4 – An Excellent 27-inch Screen
    Sony VPL-HW50ES – Sony’s Best Home Cinema Projector
    Windows Vista : Installing and Running Applications - Launching Applications
    Most View
    Bamboo Splash - Powerful Specs And Friendly Interface
    Powered By Windows (Part 2) - Toshiba Satellite U840 Series, Philips E248C3 MODA Lightframe Monitor & HP Envy Spectre 14
    MSI X79A-GD65 8D - Power without the Cost
    Canon EOS M With Wonderful Touchscreen Interface (Part 1)
    Windows Server 2003 : Building an Active Directory Structure (part 1) - The First Domain
    Personalize Your iPhone Case
    Speed ​​up browsing with a faster DNS
    Using and Configuring Public Folder Sharing
    Extending the Real-Time Communications Functionality of Exchange Server 2007 : Installing OCS 2007 (part 1)
    Google, privacy & you (Part 1)
    iPhone Application Development : Making Multivalue Choices with Pickers - Understanding Pickers
    Microsoft Surface With Windows RT - Truly A Unique Tablet
    Network Configuration & Troubleshooting (Part 1)
    Panasonic Lumix GH3 – The Fastest Touchscreen-Camera (Part 2)
    Programming Microsoft SQL Server 2005 : FOR XML Commands (part 3) - OPENXML Enhancements in SQL Server 2005
    Exchange Server 2010 : Track Exchange Performance (part 2) - Test the Performance Limitations in a Lab
    Extra Network Hardware Round-Up (Part 2) - NAS Drives, Media Center Extenders & Games Consoles
    Windows Server 2003 : Planning a Host Name Resolution Strategy - Understanding Name Resolution Requirements
    Google’s Data Liberation Front (Part 2)
    Datacolor SpyderLensCal (Part 1)