C#idiomatic C#
data-parallelism-task-parallel-librarystandard/parallel-programming

Async and concurrency

Data Parallelism (Task Parallel Library)

Read how the Task Parallel Library (TPL) supports data parallelism to do the same operation concurrently on a source collection or array's elements in .NET.

  • page standard/parallel-programming/data-parallelism-task-parallel-library
  • updated 03/30/2017

Data parallelism refers to scenarios in which the same operation is performed concurrently (that is, in parallel) on elements in a source collection or array. In data parallel operations, the source collection is partitioned so that multiple threads can operate on different segments concurrently.

The Task Parallel Library (TPL) supports data parallelism through the System.Threading.Tasks.Parallel class. This class provides method-based parallel implementations of for and foreach loops (For and For Each in Visual Basic). You write the loop logic for a System.Threading.Tasks.Parallel.For or System.Threading.Tasks.Parallel.ForEach loop much as you would write a sequential loop. You do not have to create threads or queue work items. In basic loops, you do not have to take locks. The TPL handles all the low-level work for you. For in-depth information about the use of System.Threading.Tasks.Parallel.For and System.Threading.Tasks.Parallel.ForEach, download the document Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4. The following code example shows a simple foreach loop and its parallel equivalent.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl/cs/tpl.cs20

csharp
namespace TPL
{
    using System;
    using System.Threading.Tasks;

    class Program
    {
        static void Main(string[] args)
        {
            //  WaitOnTasks();
            //  HandleExceptions();
            //  RethrowAllExceptions();

            Console.ReadKey();
        }

        class Test
        {
            public string Name { get; set; }
            public double Number { get; set; }
        }

        //<snippet13>
        static string[] GetValidExtensions(string path)
        {
            if (path == @"C:\")
                throw new ArgumentException("The system root is not a valid path.");

            return new string[10];
        }
        //</snippet13>
    }

    // 14 was deleted, probably because it duplicated cancellation snippet

    // 15 was moved to tpl_cancellation and so number is available in tpl

    //<snippet16>
    public class TreeWalk
    {
        static void Main()
        {
            Tree<MyClass> tree = new Tree<MyClass>();

            // ...populate tree (left as an exercise)

            // Define the Action to perform on each node.
            Action<MyClass> myAction = x => Console.WriteLine($"{x.Name} : {x.Number}");

            // Traverse the tree with parallel tasks.
            DoTree(tree, myAction);
        }

        public class MyClass
        {
            public string Name { get; set; }
            public int Number { get; set; }
        }
        public class Tree<T>
        {
            public Tree<T> Left;
            public Tree<T> Right;
            public T Data;
        }

        // By using tasks explicitly.
        public static void DoTree<T>(Tree<T> tree, Action<T> action)
        {
            if (tree == null) return;
            var left = Task.Factory.StartNew(() => DoTree(tree.Left, action));
            var right = Task.Factory.StartNew(() => DoTree(tree.Right, action));
            action(tree.Data);

            try
            {
                Task.WaitAll(left, right);
            }
            catch (AggregateException)
            {
                //handle exceptions here
            }
        }

        // By using Parallel.Invoke
        public static void DoTree2<T>(Tree<T> tree, Action<T> action)
        {
            if (tree == null) return;
            Parallel.Invoke(
                () => DoTree2(tree.Left, action),
                () => DoTree2(tree.Right, action),
                () => action(tree.Data)
            );
        }
    }

    //</snippet16>

    //not used
    //<snippet17>
    public class OrderPreservation
    {
    }

    //</snippet17>

    class InvokeDemo
    {

        static void MethodA() { }
        static void MethodB() { }
        static void MethodC() { }
        static void DoSomeWork(int i) { }
        static void Process(int i) { }
        static void DoSomeWork() { }
        static void DoSomeOtherWork() { }

        static int[] sourceCollection = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        static void Main()
        {

            int startIndex = 0;
            int endIndex = 10;
            //<snippet18>
            Parallel.For(startIndex, endIndex, (currentIndex) => DoSomeWork(currentIndex));
            //</snippet18>

            //<snippet19>
            Parallel.Invoke(
                        () => MethodA(),
                        () => MethodB(),
                        () => MethodC());
            //</snippet19>

            //<snippet20>
            // Sequential version
            foreach (var item in sourceCollection)
            {
                Process(item);
            }

            // Parallel equivalent
            Parallel.ForEach(sourceCollection, item => Process(item));
            //</snippet20>

            //<snippet21>
            Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
            //</snippet21>
        }
    }

    //}
}

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl/vb/tpl_vb.vb20

vb

Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks




Class Program

    Shared Sub Main(ByVal args() As String)

        '  WaitOnTasks()
        ' HandleExceptions()


        Console.ReadKey()
    End Sub



End Class



Class IntroCancellation

    Shared Function GetFileData() As Byte()
        Dim b(10) As Byte
        Return b
    End Function
    Shared Function Analyze(ByVal input As Byte()) As Double()
        Dim d(10) As Double
        Return d
    End Function

    Shared Function Summarize(ByVal d As Double()) As String

        Return "looks good"
    End Function

    Shared Sub MethodA()

    End Sub
    Shared Sub MethodB()

    End Sub
    Shared Sub MethodC()

    End Sub
    Shared Sub DoSomeWork(ByVal i As Integer)
    End Sub


    Shared Sub Process(ByVal i As Integer)

    End Sub

    Shared Sub DoSomeWork()

    End Sub
    Shared Sub DoSomeOtherWork()

    End Sub

    Shared Function DoComputation1() As Object
        DoComputation1 = New Object()
    End Function

    Shared Function DoComputation2() As Double
        Return 1.0
    End Function

    Shared Function DoComputation3() As Double
        Return 1.0
    End Function


    Shared sourceCollection() As Integer = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

    Shared Sub Test()

        Dim startIndex = 0
        Dim endIndex = 10

        '<snippet18>
        Parallel.For(startIndex, endIndex, Sub(currentIndex) DoSomeWork(currentIndex))

        '</snippet18>

        '<snippet19>
        Parallel.Invoke(
            Sub() MethodA(),
            Sub() MethodB(),
            Sub() MethodC())
        '</snippet19>

        '<snippet20>
        ' Sequential version        
        For Each item In sourceCollection
            Process(item)
        Next

        ' Parallel equivalent
        Parallel.ForEach(sourceCollection, Sub(item) Process(item))
        '</snippet20>

        '<snippet21>
        Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())
        '</snippet21>

        '<snippet22>
        Dim taskArray(3) As Task(Of Double)

        taskArray(0) = Task(Of Double).Factory.StartNew(Function() DoComputation1())
        taskArray(1) = Task(Of Double).Factory.StartNew(Function() DoComputation2())
        taskArray(2) = Task(Of Double).Factory.StartNew(Function() DoComputation3())


        Dim results(taskArray.Length - 1) As Double
        For i As Integer = 0 To taskArray.Length - 1
            results(i) = taskArray(i).Result
        Next
        '</snippet22>

        '<snippet23>
        Dim getData = New Task(Of Byte())(Function() GetFileData())
        Dim analyzeData As Task(Of Double()) = getData.ContinueWith(Function(x) Analyze(x.Result))
        Dim reportData As Task(Of String) = analyzeData.ContinueWith(Function(y) Summarize(y.Result))

        getData.Start()

        System.IO.File.WriteAllText("C:\reportFolder\report.txt", reportData.Result)
        '</snippet23>


        '<snippet24>

        Dim myTasks(3) As Task

        Task.Factory.StartNew(Sub() MethodA())
        Task.Factory.StartNew(Sub() MethodB())
        Task.Factory.StartNew(Sub() MethodC())


        Task.WaitAll(myTasks)

    End Sub
    '</snippet24>

End Class

When a parallel loop runs, the TPL partitions the data source so that the loop can operate on multiple parts concurrently. Behind the scenes, the Task Scheduler partitions the task based on system resources and workload. When possible, the scheduler redistributes work among multiple threads and processors if the workload becomes unbalanced.

Both the System.Threading.Tasks.Parallel.For and System.Threading.Tasks.Parallel.ForEach methods have several overloads that let you stop or break loop execution, monitor the state of the loop on other threads, maintain thread-local state, finalize thread-local objects, control the degree of concurrency, and so on. The helper types that enable this functionality include System.Threading.Tasks.ParallelLoopState, System.Threading.Tasks.ParallelOptions, System.Threading.Tasks.ParallelLoopResult, System.Threading.CancellationToken, and System.Threading.CancellationTokenSource.

For more information, see Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4.

Data parallelism with declarative, or query-like, syntax is supported by PLINQ. For more information, see Parallel LINQ (PLINQ).

Title Description
How to: Write a Simple Parallel.For Loop Describes how to write a System.Threading.Tasks.Parallel.For loop over any array or indexable System.Collections.Generic.IEnumerable source collection.
How to: Write a Simple Parallel.ForEach Loop Describes how to write a System.Threading.Tasks.Parallel.ForEach loop over any System.Collections.Generic.IEnumerable source collection.
How to: Stop or Break from a Parallel.For Loop Describes how to stop or break from a parallel loop so that all threads are informed of the action.
How to: Write a Parallel.For Loop with Thread-Local Variables Describes how to write a System.Threading.Tasks.Parallel.For loop in which each thread maintains a private variable that is not visible to any other threads, and how to synchronize the results from all threads when the loop completes.
How to: Write a Parallel.ForEach Loop with Partition-Local Variables Describes how to write a System.Threading.Tasks.Parallel.ForEach loop in which each thread maintains a private variable that is not visible to any other threads, and how to synchronize the results from all threads when the loop completes.
How to: Cancel a Parallel.For or ForEach Loop Describes how to cancel a parallel loop by using a System.Threading.CancellationToken
How to: Speed Up Small Loop Bodies Describes one way to speed up execution when a loop body is very small.
Task Parallel Library (TPL) Provides an overview of the Task Parallel Library.
Parallel Programming Introduces Parallel Programming in the .NET Framework.

See also