C#idiomatic C#
task-based-asynchronous-programmingstandard/parallel-programming

Async and concurrency

Task-based asynchronous programming

In this article, learn about task-based asynchronous programming through the Task Parallel Library (TPL) in .NET.

  • page standard/parallel-programming/task-based-asynchronous-programming
  • updated 10/22/2025

The Task Parallel Library (TPL) is based on the concept of a task, which represents an asynchronous operation. In some ways, a task resembles a thread or System.Threading.ThreadPool work item but at a higher level of abstraction. The term task parallelism refers to one or more independent tasks running concurrently. Tasks provide two primary benefits:

  • More efficient and more scalable use of system resources.

    Behind the scenes, tasks are queued to the System.Threading.ThreadPool, which has been enhanced with algorithms that determine and adjust to the number of threads. These algorithms provide load balancing to maximize throughput. This process makes tasks relatively lightweight, and you can create many of them to enable fine-grained parallelism.

  • More programmatic control than is possible with a thread or work item.

    Tasks and the framework built around them provide a rich set of APIs that support waiting, cancellation, continuations, robust exception handling, detailed status, custom scheduling, and more.

For both reasons, TPL is the preferred API for writing multi-threaded, asynchronous, and parallel code in .NET.

Creating and running tasks implicitly

The System.Threading.Tasks.Parallel.Invoke method provides a convenient way to run any number of arbitrary statements concurrently. Just pass in an System.Action delegate for each item of work. The easiest way to create these delegates is to use lambda expressions. The lambda expression can either call a named method or provide the code inline. The following example shows a basic System.Threading.Tasks.Parallel.Invoke call that creates and starts two tasks that run concurrently. The first task is represented by a lambda expression that calls a method named DoSomeWork, and the second task is represented by a lambda expression that calls a method named DoSomeOtherWork.

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

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.vb21

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

For more information, see How to: Use Parallel.Invoke to Execute Parallel Operations.

For greater control over task execution or to return a value from the task, you must work with System.Threading.Tasks.Task objects more explicitly.

Creating and running tasks explicitly

A task that doesn't return a value is represented by the System.Threading.Tasks.Task class. A task that returns a value is represented by the System.Threading.Tasks.Task class, which inherits from System.Threading.Tasks.Task. The task object handles the infrastructure details and provides methods and properties that are accessible from the calling thread throughout the lifetime of the task. For example, you can access the System.Threading.Tasks.Task.Status property of a task at any time to determine whether it has started running, ran to completion, was canceled, or has thrown an exception. The status is represented by a System.Threading.Tasks.TaskStatus enumeration.

When you create a task, you give it a user delegate that encapsulates the code that the task will execute. The delegate can be expressed as a named delegate, an anonymous method, or a lambda expression. Lambda expressions can contain a call to a named method, as shown in the following example. The example includes a call to the System.Threading.Tasks.Task.Wait method to ensure that the task completes execution before the console mode application ends.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/lambda1.cs1

csharp
// <Snippet1>
using System;
using System.Threading;
using System.Threading.Tasks;

public class Lambda
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Create a task and supply a user delegate by using a lambda expression.
      Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
      // Start the task.
      taskA.Start();

      // Output a message from the calling thread.
      Console.WriteLine($"Hello from thread '{Thread.CurrentThread.Name}'.");
      taskA.Wait();
   }
}
// The example displays output as follows:
//       Hello from thread 'Main'.
//       Hello from taskA.
// or
//       Hello from taskA.
//       Hello from thread 'Main'.
// </Snippet1>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/lambda1.vb1

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet1>
Imports System.Threading

Namespace Lambda
    Module Example
        Public Sub Main()
            Thread.CurrentThread.Name = "Main"

            ' Create a task and supply a user delegate by using a lambda expression. 
            Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))
            ' Start the task.
            taskA.Start()

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from thread '{0}'.",
                            Thread.CurrentThread.Name)
            taskA.Wait()
        End Sub
    End Module
    ' The example displays output like the following:
    '    Hello from thread 'Main'.
    '    Hello from taskA.
End Namespace
' </Snippet1>

You can also use the System.Threading.Tasks.Task.Run methods to create and start a task in one operation. To manage the task, the System.Threading.Tasks.Task.Run methods use the default task scheduler, regardless of which task scheduler is associated with the current thread. The System.Threading.Tasks.Task.Run methods are the preferred way to create and start tasks when more control over the creation and scheduling of the task isn't needed.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/run1.cs2

csharp
// <Snippet2>
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Run;

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Define and run the task.
      Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));

      // Output a message from the calling thread.
      Console.WriteLine($"Hello from thread '{Thread.CurrentThread.Name}'.");
      taskA.Wait();
   }
}
// The example displays output as follows:
//       Hello from thread 'Main'.
//       Hello from taskA.
// or
//       Hello from taskA.
//       Hello from thread 'Main'.
// </Snippet2>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/run1.vb2

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet2>
Imports System.Threading

Namespace Run
    Module Example
        Public Sub Main()
            Thread.CurrentThread.Name = "Main"

            Dim taskA As Task = Task.Run(Sub() Console.WriteLine("Hello from taskA."))

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from thread '{0}'.",
                            Thread.CurrentThread.Name)
            taskA.Wait()
        End Sub
    End Module
    ' The example displays output like the following:
    '    Hello from thread 'Main'.
    '    Hello from taskA.
End Namespace
' </Snippet2>

You can also use the System.Threading.Tasks.TaskFactory.StartNew method to create and start a task in one operation. As shown in the following example, you can use this method when:

  • Creation and scheduling don't have to be separated and you require additional task creation options or the use of a specific scheduler.

  • You need to pass additional state into the task that you can retrieve through its System.Threading.Tasks.Task.AsyncState property.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/asyncstate.cs23

csharp
// <Snippet23>
using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskIntro;

class CustomData
{
    public long CreationTime;
    public int Name;
    public int ThreadNum;
}

public class AsyncState
{
    public static void Main()
    {
        Task[] taskArray = new Task[10];
        for (int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = Task.Factory.StartNew((Object obj) =>
            {
                CustomData data = obj as CustomData;
                if (data == null) return;

                data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
            },
            new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks });
        }
        Task.WaitAll(taskArray);
        foreach (var task in taskArray)
        {
            var data = task.AsyncState as CustomData;
            if (data != null)
                Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                  data.Name, data.CreationTime, data.ThreadNum);
        }
    }
}
// The example displays output like the following:
//     Task #0 created at 635116412924597583, ran on thread #3.
//     Task #1 created at 635116412924607584, ran on thread #4.
//     Task #2 created at 635116412924607584, ran on thread #4.
//     Task #3 created at 635116412924607584, ran on thread #4.
//     Task #4 created at 635116412924607584, ran on thread #3.
//     Task #5 created at 635116412924607584, ran on thread #3.
//     Task #6 created at 635116412924607584, ran on thread #4.
//     Task #7 created at 635116412924607584, ran on thread #4.
//     Task #8 created at 635116412924607584, ran on thread #3.
//     Task #9 created at 635116412924607584, ran on thread #4.
// </Snippet23>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/asyncstate.vb23

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet23>
Imports System.Threading

Namespace AsyncState
    Class CustomData
        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Module Example
        Public Sub Main()
            Dim taskArray(9) As Task
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                         Dim data As CustomData = TryCast(obj, CustomData)
                                                         If data Is Nothing Then Return

                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                     End Sub,
                New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
            Next
            Task.WaitAll(taskArray)

            For Each task In taskArray
                Dim data = TryCast(task.AsyncState, CustomData)
                If data IsNot Nothing Then
                    Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                    data.Name, data.CreationTime, data.ThreadNum)
                End If
            Next
        End Sub
    End Module
    ' The example displays output like the following:
    '     Task #0 created at 635116412924597583, ran on thread #3.
    '     Task #1 created at 635116412924607584, ran on thread #4.
    '     Task #2 created at 635116412924607584, ran on thread #4.
    '     Task #3 created at 635116412924607584, ran on thread #4.
    '     Task #4 created at 635116412924607584, ran on thread #3.
    '     Task #5 created at 635116412924607584, ran on thread #3.
    '     Task #6 created at 635116412924607584, ran on thread #4.
    '     Task #7 created at 635116412924607584, ran on thread #4.
    '     Task #8 created at 635116412924607584, ran on thread #3.
    '     Task #9 created at 635116412924607584, ran on thread #4.
End Namespace
' </Snippet23>

System.Threading.Tasks.Task and System.Threading.Tasks.Task each expose a static System.Threading.Tasks.Task.Factory property that returns a default instance of System.Threading.Tasks.TaskFactory, so that you can call the method as Task.Factory.StartNew(). Also, in the following example, because the tasks are of type System.Threading.Tasks.Task, they each have a public System.Threading.Tasks.Task.Result property that contains the result of the computation. The tasks run asynchronously and might complete in any order. If the System.Threading.Tasks.Task.Result property is accessed before the computation finishes, the property blocks the calling thread until the value is available.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/result1.cs4

csharp
// <Snippet4>
using System;
using System.Threading.Tasks;

public class Result
{
   public static void Main()
   {
        Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(100.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };

        var results = new Double[taskArray.Length];
        Double sum = 0;

        for (int i = 0; i < taskArray.Length; i++) {
            results[i] = taskArray[i].Result;
            Console.Write("{0:N1} {1}", results[i],
                              i == taskArray.Length - 1 ? "= " : "+ ");
            sum += results[i];
        }
        Console.WriteLine($"{sum:N1}");
   }

   private static Double DoComputation(Double start)
   {
      Double sum = 0;
      for (var value = start; value <= start + 10; value += .1)
         sum += value;

      return sum;
   }
}
// The example displays the following output:
//        606.0 + 10,605.0 + 100,495.0 = 111,706.0
// </Snippet4>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/result1.vb4

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet4>

Namespace Result
    Module Example
        Public Sub Main()
            Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation(1.0)),
                Task(Of Double).Factory.StartNew(Function() DoComputation(100.0)),
                Task(Of Double).Factory.StartNew(Function() DoComputation(1000.0))}

            Dim results(taskArray.Length - 1) As Double
            Dim sum As Double

            For i As Integer = 0 To taskArray.Length - 1
                results(i) = taskArray(i).Result
                Console.Write("{0:N1} {1}", results(i),
                    If(i = taskArray.Length - 1, "= ", "+ "))
                sum += results(i)
            Next
            Console.WriteLine("{0:N1}", sum)
        End Sub

        Private Function DoComputation(start As Double) As Double
            Dim sum As Double
            For value As Double = start To start + 10 Step .1
                sum += value
            Next
            Return sum
        End Function
    End Module
    ' The example displays the following output:
    '       606.0 + 10,605.0 + 100,495.0 = 111,706.0
End Namespace
' </Snippet4>

For more information, see How to: Return a Value from a Task.

When you use a lambda expression to create a delegate, you have access to all the variables that are visible at that point in your source code. However, in some cases, most notably within loops, a lambda doesn't capture the variable as expected. It only captures the reference of the variable, not the value, as it mutates after each iteration. The following example illustrates the problem. It passes a loop counter to a lambda expression that instantiates a CustomData object and uses the loop counter as the object's identifier. As the output from the example shows, each CustomData object has an identical identifier.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/iteration1b.cs22

csharp
// <Snippet22>
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Example.Iterations;

class CustomData
{
   public long CreationTime;
   public int Name;
   public int ThreadNum;
}

public class IterationTwo
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in the loop
      // counter. This produces an unexpected result.
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj) => {
                                                 var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks};
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                 Console.WriteLine($"Task #{data.Name} created at {data.CreationTime} on thread #{data.ThreadNum}.");
                                               },
                                              i );
      }
      Task.WaitAll(taskArray);
   }
}
// The example displays output like the following:
//       Task #10 created at 635116418427727841 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427727841 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427737842 on thread #4.
// </Snippet22>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/iteration1b.vb22

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet22>
Imports System.Threading

Namespace IterationsTwo
    Class CustomData
        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Module Example
        Public Sub Main()
            ' Create the task object by using an Action(Of Object) to pass in the loop
            ' counter. This produces an unexpected result.
            Dim taskArray(9) As Task
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                         Dim data As New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks}
                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                         Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                         data.Name, data.CreationTime, data.ThreadNum)
                                                     End Sub,
                    i)
            Next
            Task.WaitAll(taskArray)
        End Sub
    End Module
    ' The example displays output like the following:
    '       Task #10 created at 635116418427727841 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427727841 on thread #3.
    '       Task #10 created at 635116418427747843 on thread #3.
    '       Task #10 created at 635116418427747843 on thread #3.
    '       Task #10 created at 635116418427737842 on thread #4.
End Namespace
' </Snippet22>

You can access the value on each iteration by providing a state object to a task through its constructor. The following example modifies the previous example by using the loop counter when creating the CustomData object, which, in turn, is passed to the lambda expression. As the output from the example shows, each CustomData object now has a unique identifier based on the value of the loop counter at the time the object was instantiated.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/iteration1a.cs21

csharp
// <Snippet21>
using System;
using System.Threading;
using System.Threading.Tasks;

class CustomData
{
   public long CreationTime;
   public int Name;
   public int ThreadNum;
}

public class IterationOne
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in custom data
      // to the Task constructor. This is useful when you need to capture outer variables
      // from within a loop.
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null)
                                                     return;

                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                  Console.WriteLine($"Task #{data.Name} created at {data.CreationTime} on thread #{data.ThreadNum}.");
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
// </Snippet21>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/iteration1a.vb21

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet21>
Imports System.Threading

Namespace IterationsOne
    Class CustomData
        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Module Example
        Public Sub Main()
            ' Create the task object by using an Action(Of Object) to pass in custom data
            ' to the Task constructor. This is useful when you need to capture outer variables
            ' from within a loop. 
            Dim taskArray(9) As Task
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                         Dim data As CustomData = TryCast(obj, CustomData)
                                                         If data Is Nothing Then Return

                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                         Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                         data.Name, data.CreationTime, data.ThreadNum)
                                                     End Sub,
                    New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
            Next
            Task.WaitAll(taskArray)
        End Sub
    End Module
    ' The example displays output like the following:
    '       Task #0 created at 635116412924597583 on thread #3.
    '       Task #1 created at 635116412924607584 on thread #4.
    '       Task #3 created at 635116412924607584 on thread #4.
    '       Task #4 created at 635116412924607584 on thread #4.
    '       Task #2 created at 635116412924607584 on thread #3.
    '       Task #6 created at 635116412924607584 on thread #3.
    '       Task #5 created at 635116412924607584 on thread #4.
    '       Task #8 created at 635116412924607584 on thread #4.
    '       Task #7 created at 635116412924607584 on thread #3.
    '       Task #9 created at 635116412924607584 on thread #4.
End Namespace
' </Snippet21>

This state is passed as an argument to the task delegate, and it can be accessed from the task object by using the System.Threading.Tasks.Task.AsyncState property. The following example is a variation on the previous example. It uses the System.Threading.Tasks.Task.AsyncState property to display information about the CustomData objects passed to the lambda expression.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/asyncstate.cs23

csharp
// <Snippet23>
using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskIntro;

class CustomData
{
    public long CreationTime;
    public int Name;
    public int ThreadNum;
}

public class AsyncState
{
    public static void Main()
    {
        Task[] taskArray = new Task[10];
        for (int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = Task.Factory.StartNew((Object obj) =>
            {
                CustomData data = obj as CustomData;
                if (data == null) return;

                data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
            },
            new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks });
        }
        Task.WaitAll(taskArray);
        foreach (var task in taskArray)
        {
            var data = task.AsyncState as CustomData;
            if (data != null)
                Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                  data.Name, data.CreationTime, data.ThreadNum);
        }
    }
}
// The example displays output like the following:
//     Task #0 created at 635116412924597583, ran on thread #3.
//     Task #1 created at 635116412924607584, ran on thread #4.
//     Task #2 created at 635116412924607584, ran on thread #4.
//     Task #3 created at 635116412924607584, ran on thread #4.
//     Task #4 created at 635116412924607584, ran on thread #3.
//     Task #5 created at 635116412924607584, ran on thread #3.
//     Task #6 created at 635116412924607584, ran on thread #4.
//     Task #7 created at 635116412924607584, ran on thread #4.
//     Task #8 created at 635116412924607584, ran on thread #3.
//     Task #9 created at 635116412924607584, ran on thread #4.
// </Snippet23>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/asyncstate.vb23

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet23>
Imports System.Threading

Namespace AsyncState
    Class CustomData
        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Module Example
        Public Sub Main()
            Dim taskArray(9) As Task
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                         Dim data As CustomData = TryCast(obj, CustomData)
                                                         If data Is Nothing Then Return

                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                     End Sub,
                New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
            Next
            Task.WaitAll(taskArray)

            For Each task In taskArray
                Dim data = TryCast(task.AsyncState, CustomData)
                If data IsNot Nothing Then
                    Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                    data.Name, data.CreationTime, data.ThreadNum)
                End If
            Next
        End Sub
    End Module
    ' The example displays output like the following:
    '     Task #0 created at 635116412924597583, ran on thread #3.
    '     Task #1 created at 635116412924607584, ran on thread #4.
    '     Task #2 created at 635116412924607584, ran on thread #4.
    '     Task #3 created at 635116412924607584, ran on thread #4.
    '     Task #4 created at 635116412924607584, ran on thread #3.
    '     Task #5 created at 635116412924607584, ran on thread #3.
    '     Task #6 created at 635116412924607584, ran on thread #4.
    '     Task #7 created at 635116412924607584, ran on thread #4.
    '     Task #8 created at 635116412924607584, ran on thread #3.
    '     Task #9 created at 635116412924607584, ran on thread #4.
End Namespace
' </Snippet23>

Task ID

Every task receives an integer ID that uniquely identifies it in an application domain and can be accessed by using the System.Threading.Tasks.Task.Id property. The ID is useful for viewing task information in the Visual Studio debugger Parallel Stacks and Tasks windows. The ID is created lazily, which means it isn't created until it's requested. Therefore, a task might have a different ID every time the program is run. For more information about how to view task IDs in the debugger, see Using the Tasks Window and Using the Parallel Stacks Window.

Task creation options

Most APIs that create tasks provide overloads that accept a System.Threading.Tasks.TaskCreationOptions parameter. By specifying one or more of these options, you tell the task scheduler how to schedule the task on the thread pool. Options might be combined by using a bitwise OR operation.

The following example shows a task that has the System.Threading.Tasks.TaskCreationOptions.LongRunning and System.Threading.Tasks.TaskCreationOptions.PreferFairness options:

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/taskintro.cs03

csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TPL_Intro
{
    class Program
    {
        static void Main(string[] args)
        {
            SimpleTaskCreation();
            ChildTaskSnippets();
            TaskDemo2();
            Console.ReadKey();
        }

        static void SimpleTaskCreation()
        {
            //<snippet01>
            // Create a task and supply a user delegate by using a lambda expression.
            var taskA = new Task(() => Console.WriteLine("Hello from taskA."));

            // Start the task.
            taskA.Start();

            // Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.");
            // Message from taskA should follow.

            /* Output:
             * Hello from the calling thread.
             * Hello from taskA.
             */

            //</snippet01>
        }

        static void SimpleStartNew()
        {
            //<snippet09>
            // Create and start the task in one operation.
            var taskA = Task.Factory.StartNew(() => Console.WriteLine("Hello from taskA."));

            // Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.");
            //</snippet09>
        }

        //<snippet02>

       class MyCustomData
       {
        public long CreationTime;
        public int Name;
        public int ThreadNum;
        }

    static void TaskDemo2()
    {
        // Create the task object by using an Action(Of Object) to pass in custom data
        // in the Task constructor. This is useful when you need to capture outer variables
        // from within a loop. As an experiment, try modifying this code to
        // capture i directly in the lambda, and compare results.
        Task[] taskArray = new Task[10];

        for(int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = new Task((obj) =>
                {
                                        MyCustomData mydata = (MyCustomData) obj;
                                        mydata.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                        Console.WriteLine($"Hello from Task #{mydata.Name} created at {mydata.CreationTime} running on thread #{mydata.ThreadNum}.");
                },
            new MyCustomData () {Name = i, CreationTime = DateTime.Now.Ticks}
            );
            taskArray[i].Start();
        }
    }

        //</snippet02>

        static void TaskDemo2A()
        {
            Task.Factory.StartNew(state =>
            {
                dynamic data = state;
                Console.WriteLine(data.Name);
                Console.WriteLine(data.Date);
            }, new { Name = "taskB", Date = DateTime.Today });
        }

        static void TaskDemo3()
        {
            //<snippet03>
            var task3 = new Task(() => MyLongRunningMethod(),
                                TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
            task3.Start();
            //</snippet03>
        }

        // dummy methods
        static void MyLongRunningMethod() { }
        static double DoComputation1() { return 1.0; }
        static double DoComputation2() { return 1.0; }
        static double DoComputation3() { return 1.0; }
        static byte[] GetFileData() { return new byte[100]; }
        static double[] Analyze(byte[] input) { return new double[100]; }
        static string Summarize(double[] d) { return "looks good"; }
        static void MethodA() { }
        static void MethodB() { }
        static void MethodC() { }

        static void MoreSimple()
        {

            //<snippet04>
            Task<double>[] taskArray = new Task<double>[]
               {
                   Task<double>.Factory.StartNew(() => DoComputation1()),

                   // May be written more conveniently like this:
                   Task.Factory.StartNew(() => DoComputation2()),
                   Task.Factory.StartNew(() => DoComputation3())
               };

            double[] results = new double[taskArray.Length];
            for (int i = 0; i < taskArray.Length; i++)
                results[i] = taskArray[i].Result;
            //</snippet04>

            //<snippet05>
            Task<byte[]> getData = new Task<byte[]>(() => GetFileData());
            Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result));
            Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result));

            getData.Start();

            //or...
            Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData())
                                        .ContinueWith((x) => Analyze(x.Result))
                                        .ContinueWith((y) => Summarize(y.Result));

            System.IO.File.WriteAllText(@"C:\reportFolder\report.txt", reportData.Result);

            //</snippet05>

            //<snippet06>
            Task[] tasks = new Task[3]
            {
                Task.Factory.StartNew(() => MethodA()),
                Task.Factory.StartNew(() => MethodB()),
                Task.Factory.StartNew(() => MethodC())
            };

            //Block until all tasks complete.
            Task.WaitAll(tasks);

            // Continue on this thread...
            //</snippet06>
        }

        static void ChildTaskSnippets()
        {
            //<snippet07>
            var outer = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Outer task beginning.");

                var child = Task.Factory.StartNew(() =>
                {
                    Thread.SpinWait(5000000);
                    Console.WriteLine("Detached task completed.");
                });
            });

            outer.Wait();
            Console.WriteLine("Outer task completed.");
            // The example displays the following output:
            //    Outer task beginning.
            //    Outer task completed.
            //    Detached task completed.
            //</snippet07>

            //<snippet08>
            var parent = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Parent task beginning.");

                var child = Task.Factory.StartNew(() =>
                {
                    Thread.SpinWait(5000000);
                    Console.WriteLine("Attached task completed.");
                }, TaskCreationOptions.AttachedToParent);
            });

            parent.Wait();
            Console.WriteLine("Parent task completed.");

            /* Output:
                Parent task beginning.
                Attached task completed.
                Parent task completed.
             */
            //</snippet08>

            var t = Task<int>.Factory.StartNew(() => 1);
        }
    }
}

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/tpl_intro.vb03

vb
Imports System.Threading

Namespace Introduction
    Class Program

        Public Shared Sub Main()
            SimpleTaskCreation()
            ChildTaskSnippets()
            TaskDemo2()
            Console.ReadKey()
        End Sub


        Shared Sub SimpleTaskCreation()
            '<snippet01>
            ' Create a task and supply a user delegate by using a lambda expression.
            Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))

            ' Start the task.
            taskA.Start()

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.")

            ' Output:
            ' Hello from the calling thread.
            ' Hello from taskA.

            '</snippet01>
        End Sub

        Sub SimpleStartNew()

            '<snippet09>
            ' Better: Create and start the task in one operation.
            Dim taskA = Task.Factory.StartNew(Sub() Console.WriteLine("Hello from taskA."))

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.")
            '</snippet09>
        End Sub


        '<snippet02>

        Class MyCustomData

            Public CreationTime As Long
            Public Name As Integer
            Public ThreadNum As Integer
        End Class

        Shared Sub TaskDemo2()
            ' Create the task object by using an Action(Of Object) to pass in custom data
            ' in the Task constructor. This is useful when you need to capture outer variables
            ' from within a loop.
            ' As an experiment, try modifying this code to capture i directly in the lambda,
            ' and compare results.
            Dim taskArray() As Task
            ReDim taskArray(10)
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = New Task(Sub(obj As Object)
                                            Dim mydata = CType(obj, MyCustomData)
                                            mydata.ThreadNum = Environment.CurrentManagedThreadId
                                            Console.WriteLine("Hello from Task #{0} created at {1} running on thread #{2}.",
                                                            mydata.Name, mydata.CreationTime, mydata.ThreadNum)
                                        End Sub,
                New MyCustomData With {.Name = i, .CreationTime = Date.Now.Ticks}
                )
                taskArray(i).Start()
            Next

        End Sub

        '</snippet02>

        Sub TaskDemo3()
            '<snippet03>

            Dim task3 = New Task(Sub() MyLongRunningMethod(),
                                    TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
            task3.Start()
            '</snippet03>

        End Sub

        ' dummy methods
        Sub MyLongRunningMethod()
        End Sub
        Function DoComputation1() As Double
            Return 1.0
        End Function
        Function DoComputation2() As Double
            DoComputation2 = 1.0
        End Function
        Function DoComputation3() As Double
            DoComputation3 = 1.0
        End Function
        Function GetFileData() As Byte()
            Dim filedata As Byte() = {10}
            Return filedata
        End Function
        Function Analyze(ByVal input() As Byte) As Double()
            Dim result As Double() = {1.0}
            Return result
        End Function
        Function Summarize(ByVal d As Double) As String
            Return "Looks good."
        End Function

        Sub MethodA()
        End Sub
        Sub MethodB()
        End Sub

        Sub MethodC()
        End Sub

        Sub MoreSimple()

            '<snippet04>
            Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation1()),
                Task(Of Double).Factory.StartNew(Function() DoComputation2()),
                Task(Of Double).Factory.StartNew(Function() DoComputation3())}


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

            '<snippet05>
            Dim getData As Task(Of Byte()) = 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 As Task(Of Double)) Summarize(y.Result))

            getData.Start()

            IO.File.WriteAllText("C:\reportFolder\report.txt", reportData.Result)

            '</snippet05>


            '<snippet06>
            Dim tasks() =
            {
                Task.Factory.StartNew(Sub() MethodA()),
                Task.Factory.StartNew(Sub() MethodB()),
                Task.Factory.StartNew(Sub() MethodC())
            }

            ' Block until all tasks complete.
            Task.WaitAll(tasks)

            ' Continue on this thread...
            '</snippet06>

        End Sub

        Shared Sub ChildTaskSnippets()

            '<snippet07>
            Dim outer = Task.Factory.StartNew(Sub()
                                                  Console.WriteLine("Outer task beginning.")
                                                  Dim child = Task.Factory.StartNew(Sub()
                                                                                        Thread.SpinWait(5000000)
                                                                                        Console.WriteLine("Detached task completed.")
                                                                                    End Sub)
                                              End Sub)
            outer.Wait()
            Console.WriteLine("Outer task completed.")
            ' The example displays the following output:
            '     Outer task beginning.
            '     Outer task completed.
            '    Detached child completed.
            '</snippet07>

            '<snippet08>
            Dim parent = Task.Factory.StartNew(Sub()
                                                   Console.WriteLine("Parent task beginning.")
                                                   Dim child = Task.Factory.StartNew(Sub()
                                                                                         Thread.SpinWait(5000000)
                                                                                         Console.WriteLine("Attached task completed.")
                                                                                     End Sub,
                                                        TaskCreationOptions.AttachedToParent)

                                               End Sub)
            outer.Wait()
            Console.WriteLine("Parent task completed.")

            ' Output:
            '     Parent task beginning.
            '     Attached task completed.
            '     Parent task completed.
            '</snippet08>



            Dim t = Task(Of Integer).Factory.StartNew(Function() 1)


        End Sub


    End Class
End Namespace

Tasks, threads, and culture

Each thread has an associated culture and UI culture, which are defined by the System.Threading.Thread.CurrentCulture and System.Threading.Thread.CurrentUICulture properties, respectively. A thread's culture is used in operations such as formatting, parsing, sorting, and string comparison operations. A thread's UI culture is used in resource lookup.

The system culture defines the default culture and UI culture of a thread. However, you can specify a default culture for all the threads in an application domain by using the System.Globalization.CultureInfo.DefaultThreadCurrentCulture and System.Globalization.CultureInfo.DefaultThreadCurrentUICulture properties. If you explicitly set a thread's culture and launch a new thread, the new thread doesn't inherit the culture of the calling thread; instead, its culture is the default system culture. However, in task-based programming, tasks use the calling thread's culture, even if the task runs asynchronously on a different thread.

The following example provides a simple illustration. It changes the app's current culture to French (France). If French (France) is already the current culture, it changes to English (United States). It then invokes a delegate named formatDelegate that returns some numbers formatted as currency values in the new culture. Whether the delegate is invoked by a task either synchronously or asynchronously, the task uses the culture of the calling thread.

sample · csharpdocs/standard/parallel-programming/snippets/cs/asyncculture1.cs1

csharp
// <Snippet1>
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
       decimal[] values = { 163025412.32m, 18905365.59m };
       string formatString = "C2";
       Func<String> formatDelegate = () => { string output = String.Format("Formatting using the {0} culture on thread {1}.\n",
                                                                           CultureInfo.CurrentCulture.Name,
                                                                           Thread.CurrentThread.ManagedThreadId);
                                             foreach (var value in values)
                                                output += String.Format("{0}   ", value.ToString(formatString));

                                             output += Environment.NewLine;
                                             return output;
                                           };

       Console.WriteLine($"The example is running on thread {Thread.CurrentThread.ManagedThreadId}");
       // Make the current culture different from the system culture.
       Console.WriteLine($"The current culture is {CultureInfo.CurrentCulture.Name}");
       if (CultureInfo.CurrentCulture.Name == "fr-FR")
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
       else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

       Console.WriteLine($"Changed the current culture to {CultureInfo.CurrentCulture.Name}.\n");

       // Execute the delegate synchronously.
       Console.WriteLine("Executing the delegate synchronously:");
       Console.WriteLine(formatDelegate());

       // Call an async delegate to format the values using one format string.
       Console.WriteLine("Executing a task asynchronously:");
       var t1 = Task.Run(formatDelegate);
       Console.WriteLine(t1.Result);

       Console.WriteLine("Executing a task synchronously:");
       var t2 = new Task<String>(formatDelegate);
       t2.RunSynchronously();
       Console.WriteLine(t2.Result);
   }
}
// The example displays the following output:
//         The example is running on thread 1
//         The current culture is en-US
//         Changed the current culture to fr-FR.
//
//         Executing the delegate synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task asynchronously:
//         Formatting using the fr-FR culture on thread 3.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
// </Snippet1>

sample · vbnetdocs/standard/parallel-programming/snippets/vb/asyncculture1.vb1

vbnet
' Visual Basic .NET Document
Option Strict On
Option Infer On

' <Snippet1>
Imports System.Globalization
Imports System.Threading

Module Example
    Public Sub Main()
        Dim values() As Decimal = {163025412.32D, 18905365.59D}
        Dim formatString As String = "C2"
        Dim formatDelegate As Func(Of String) = Function()
                                                    Dim output As String = String.Format("Formatting using the {0} culture on thread {1}.",
                                                                                         CultureInfo.CurrentCulture.Name,
                                                                                         Thread.CurrentThread.ManagedThreadId)
                                                    output += Environment.NewLine
                                                    For Each value In values
                                                        output += String.Format("{0}   ", value.ToString(formatString))
                                                    Next
                                                    output += Environment.NewLine
                                                    Return output
                                                End Function

        Console.WriteLine("The example is running on thread {0}",
                          Thread.CurrentThread.ManagedThreadId)
        ' Make the current culture different from the system culture.
        Console.WriteLine("The current culture is {0}",
                          CultureInfo.CurrentCulture.Name)
        If CultureInfo.CurrentCulture.Name = "fr-FR" Then
            Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        Else
            Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-FR")
        End If
        Console.WriteLine("Changed the current culture to {0}.",
                          CultureInfo.CurrentCulture.Name)
        Console.WriteLine()

        ' Execute the delegate synchronously.
        Console.WriteLine("Executing the delegate synchronously:")
        Console.WriteLine(formatDelegate())

        ' Call an async delegate to format the values using one format string.
        Console.WriteLine("Executing a task asynchronously:")
        Dim t1 = Task.Run(formatDelegate)
        Console.WriteLine(t1.Result)

        Console.WriteLine("Executing a task synchronously:")
        Dim t2 = New Task(Of String)(formatDelegate)
        t2.RunSynchronously()
        Console.WriteLine(t2.Result)
    End Sub
End Module

' The example displays the following output:
'
'          The example is running on thread 1
'          The current culture is en-US
'          Changed the current culture to fr-FR.
'
'          Executing the delegate synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task asynchronously:
'          Formatting Imports the fr-FR culture on thread 3.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
' </Snippet1>

For more information on asynchronous tasks and culture, see the "Culture and asynchronous task-based operations" section in the System.Globalization.CultureInfo article.

Creating task continuations

The System.Threading.Tasks.Task.ContinueWith and System.Threading.Tasks.Task.ContinueWith methods let you specify a task to start when the antecedent task finishes. The delegate of the continuation task is passed a reference to the antecedent task so that it can examine the antecedent task's status. And by retrieving the value of the System.Threading.Tasks.Task.Result property, you can use the output of the antecedent as input for the continuation.

In the following example, the getData task is started by a call to the System.Threading.Tasks.TaskFactory.StartNew%28System.Func%7B%7D%29 method. The processData task is started automatically when getData finishes, and displayData is started when processData finishes. getData produces an integer array, which is accessible to the processData task through the getData task's System.Threading.Tasks.Task.Result property. The processData task processes that array and returns a result whose type is inferred from the return type of the lambda expression passed to the System.Threading.Tasks.Task.ContinueWith%28System.Func%7BSystem.Threading.Tasks.Task%7B%7D%2C%7D%29 method. The displayData task executes automatically when processData finishes, and the System.Tuple object returned by the processData lambda expression is accessible to the displayData task through the processData task's System.Threading.Tasks.Task.Result property. The displayData task takes the result of the processData task. It produces a result whose type is inferred in a similar manner, and which is made available to the program in the System.Threading.Tasks.Task.Result property.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/continuations1.cs5

csharp
// <Snippet5>
using System;
using System.Threading.Tasks;

public class ContinuationOne
{
   public static void Main()
   {
      var getData = Task.Factory.StartNew(() => {
                                             Random rnd = new Random();
                                             int[] values = new int[100];
                                             for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                values[ctr] = rnd.Next();

                                             return values;
                                          } );
      var processData = getData.ContinueWith((x) => {
                                                int n = x.Result.Length;
                                                long sum = 0;
                                                double mean;

                                                for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                                   sum += x.Result[ctr];

                                                mean = sum / (double) n;
                                                return Tuple.Create(n, sum, mean);
                                             } );
      var displayData = processData.ContinueWith((x) => {
                                                    return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2,
                                                                         x.Result.Item3);
                                                 } );
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
// </Snippet5>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/continuations1.vb5

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet5>

Namespace ContinuationsOne
    Module Example
        Public Sub Main()
            Dim getData = Task.Factory.StartNew(Function()
                                                    Dim rnd As New Random()
                                                    Dim values(99) As Integer
                                                    For ctr = 0 To values.GetUpperBound(0)
                                                        values(ctr) = rnd.Next()
                                                    Next
                                                    Return values
                                                End Function)
            Dim processData = getData.ContinueWith(Function(x)
                                                       Dim n As Integer = x.Result.Length
                                                       Dim sum As Long
                                                       Dim mean As Double

                                                       For ctr = 0 To x.Result.GetUpperBound(0)
                                                           sum += x.Result(ctr)
                                                       Next
                                                       mean = sum / n
                                                       Return Tuple.Create(n, sum, mean)
                                                   End Function)
            Dim displayData = processData.ContinueWith(Function(x)
                                                           Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                                   x.Result.Item1, x.Result.Item2,
                                                                                   x.Result.Item3)
                                                       End Function)
            Console.WriteLine(displayData.Result)
        End Sub
    End Module
    ' The example displays output like the following:
    '   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
End Namespace
' </Snippet5>

Because System.Threading.Tasks.Task.ContinueWith is an instance method, you can chain method calls together instead of instantiating a System.Threading.Tasks.Task object for each antecedent task. The following example is functionally identical to the previous one, except that it chains together calls to the System.Threading.Tasks.Task.ContinueWith method. The System.Threading.Tasks.Task object returned by the chain of method calls is the final continuation task.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/continuations2.cs24

csharp
// <Snippet24>
using System;
using System.Threading.Tasks;

public class ContinuationTwo
{
   public static void Main()
   {
      var displayData = Task.Factory.StartNew(() => {
                                                 Random rnd = new Random();
                                                 int[] values = new int[100];
                                                 for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                    values[ctr] = rnd.Next();

                                                 return values;
                                              } ).
                        ContinueWith((x) => {
                                        int n = x.Result.Length;
                                        long sum = 0;
                                        double mean;

                                        for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                           sum += x.Result[ctr];

                                        mean = sum / (double) n;
                                        return Tuple.Create(n, sum, mean);
                                     } ).
                        ContinueWith((x) => {
                                        return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2,
                                                             x.Result.Item3);
                                     } );
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
// </Snippet24>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/continuations2.vb24

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet24>

Namespace ContinuationsTwo
    Module Example
        Public Sub Main()
            Dim displayData = Task.Factory.StartNew(Function()
                                                        Dim rnd As New Random()
                                                        Dim values(99) As Integer
                                                        For ctr = 0 To values.GetUpperBound(0)
                                                            values(ctr) = rnd.Next()
                                                        Next
                                                        Return values
                                                    End Function). _
            ContinueWith(Function(x)
                             Dim n As Integer = x.Result.Length
                             Dim sum As Long
                             Dim mean As Double

                             For ctr = 0 To x.Result.GetUpperBound(0)
                                 sum += x.Result(ctr)
                             Next
                             mean = sum / n
                             Return Tuple.Create(n, sum, mean)
                         End Function). _
            ContinueWith(Function(x)
                             Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                 x.Result.Item1, x.Result.Item2,
                                                 x.Result.Item3)
                         End Function)
            Console.WriteLine(displayData.Result)
        End Sub
    End Module
    ' The example displays output like the following:
    '   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
End Namespace
' </Snippet24>

The System.Threading.Tasks.TaskFactory.ContinueWhenAll and System.Threading.Tasks.TaskFactory.ContinueWhenAny methods enable you to continue from multiple tasks.

For more information, see Chaining Tasks by Using Continuation Tasks.

Creating detached child tasks

When user code that's running in a task creates a new task and doesn't specify the System.Threading.Tasks.TaskCreationOptions.AttachedToParent option, the new task isn't synchronized with the parent task in any special way. This type of non-synchronized task is called a detached nested task or detached child task. The following example shows a task that creates one detached child task:

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/taskintro.cs07

csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TPL_Intro
{
    class Program
    {
        static void Main(string[] args)
        {
            SimpleTaskCreation();
            ChildTaskSnippets();
            TaskDemo2();
            Console.ReadKey();
        }

        static void SimpleTaskCreation()
        {
            //<snippet01>
            // Create a task and supply a user delegate by using a lambda expression.
            var taskA = new Task(() => Console.WriteLine("Hello from taskA."));

            // Start the task.
            taskA.Start();

            // Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.");
            // Message from taskA should follow.

            /* Output:
             * Hello from the calling thread.
             * Hello from taskA.
             */

            //</snippet01>
        }

        static void SimpleStartNew()
        {
            //<snippet09>
            // Create and start the task in one operation.
            var taskA = Task.Factory.StartNew(() => Console.WriteLine("Hello from taskA."));

            // Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.");
            //</snippet09>
        }

        //<snippet02>

       class MyCustomData
       {
        public long CreationTime;
        public int Name;
        public int ThreadNum;
        }

    static void TaskDemo2()
    {
        // Create the task object by using an Action(Of Object) to pass in custom data
        // in the Task constructor. This is useful when you need to capture outer variables
        // from within a loop. As an experiment, try modifying this code to
        // capture i directly in the lambda, and compare results.
        Task[] taskArray = new Task[10];

        for(int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = new Task((obj) =>
                {
                                        MyCustomData mydata = (MyCustomData) obj;
                                        mydata.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                        Console.WriteLine($"Hello from Task #{mydata.Name} created at {mydata.CreationTime} running on thread #{mydata.ThreadNum}.");
                },
            new MyCustomData () {Name = i, CreationTime = DateTime.Now.Ticks}
            );
            taskArray[i].Start();
        }
    }

        //</snippet02>

        static void TaskDemo2A()
        {
            Task.Factory.StartNew(state =>
            {
                dynamic data = state;
                Console.WriteLine(data.Name);
                Console.WriteLine(data.Date);
            }, new { Name = "taskB", Date = DateTime.Today });
        }

        static void TaskDemo3()
        {
            //<snippet03>
            var task3 = new Task(() => MyLongRunningMethod(),
                                TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
            task3.Start();
            //</snippet03>
        }

        // dummy methods
        static void MyLongRunningMethod() { }
        static double DoComputation1() { return 1.0; }
        static double DoComputation2() { return 1.0; }
        static double DoComputation3() { return 1.0; }
        static byte[] GetFileData() { return new byte[100]; }
        static double[] Analyze(byte[] input) { return new double[100]; }
        static string Summarize(double[] d) { return "looks good"; }
        static void MethodA() { }
        static void MethodB() { }
        static void MethodC() { }

        static void MoreSimple()
        {

            //<snippet04>
            Task<double>[] taskArray = new Task<double>[]
               {
                   Task<double>.Factory.StartNew(() => DoComputation1()),

                   // May be written more conveniently like this:
                   Task.Factory.StartNew(() => DoComputation2()),
                   Task.Factory.StartNew(() => DoComputation3())
               };

            double[] results = new double[taskArray.Length];
            for (int i = 0; i < taskArray.Length; i++)
                results[i] = taskArray[i].Result;
            //</snippet04>

            //<snippet05>
            Task<byte[]> getData = new Task<byte[]>(() => GetFileData());
            Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result));
            Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result));

            getData.Start();

            //or...
            Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData())
                                        .ContinueWith((x) => Analyze(x.Result))
                                        .ContinueWith((y) => Summarize(y.Result));

            System.IO.File.WriteAllText(@"C:\reportFolder\report.txt", reportData.Result);

            //</snippet05>

            //<snippet06>
            Task[] tasks = new Task[3]
            {
                Task.Factory.StartNew(() => MethodA()),
                Task.Factory.StartNew(() => MethodB()),
                Task.Factory.StartNew(() => MethodC())
            };

            //Block until all tasks complete.
            Task.WaitAll(tasks);

            // Continue on this thread...
            //</snippet06>
        }

        static void ChildTaskSnippets()
        {
            //<snippet07>
            var outer = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Outer task beginning.");

                var child = Task.Factory.StartNew(() =>
                {
                    Thread.SpinWait(5000000);
                    Console.WriteLine("Detached task completed.");
                });
            });

            outer.Wait();
            Console.WriteLine("Outer task completed.");
            // The example displays the following output:
            //    Outer task beginning.
            //    Outer task completed.
            //    Detached task completed.
            //</snippet07>

            //<snippet08>
            var parent = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Parent task beginning.");

                var child = Task.Factory.StartNew(() =>
                {
                    Thread.SpinWait(5000000);
                    Console.WriteLine("Attached task completed.");
                }, TaskCreationOptions.AttachedToParent);
            });

            parent.Wait();
            Console.WriteLine("Parent task completed.");

            /* Output:
                Parent task beginning.
                Attached task completed.
                Parent task completed.
             */
            //</snippet08>

            var t = Task<int>.Factory.StartNew(() => 1);
        }
    }
}

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/tpl_intro.vb07

vb
Imports System.Threading

Namespace Introduction
    Class Program

        Public Shared Sub Main()
            SimpleTaskCreation()
            ChildTaskSnippets()
            TaskDemo2()
            Console.ReadKey()
        End Sub


        Shared Sub SimpleTaskCreation()
            '<snippet01>
            ' Create a task and supply a user delegate by using a lambda expression.
            Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))

            ' Start the task.
            taskA.Start()

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.")

            ' Output:
            ' Hello from the calling thread.
            ' Hello from taskA.

            '</snippet01>
        End Sub

        Sub SimpleStartNew()

            '<snippet09>
            ' Better: Create and start the task in one operation.
            Dim taskA = Task.Factory.StartNew(Sub() Console.WriteLine("Hello from taskA."))

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.")
            '</snippet09>
        End Sub


        '<snippet02>

        Class MyCustomData

            Public CreationTime As Long
            Public Name As Integer
            Public ThreadNum As Integer
        End Class

        Shared Sub TaskDemo2()
            ' Create the task object by using an Action(Of Object) to pass in custom data
            ' in the Task constructor. This is useful when you need to capture outer variables
            ' from within a loop.
            ' As an experiment, try modifying this code to capture i directly in the lambda,
            ' and compare results.
            Dim taskArray() As Task
            ReDim taskArray(10)
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = New Task(Sub(obj As Object)
                                            Dim mydata = CType(obj, MyCustomData)
                                            mydata.ThreadNum = Environment.CurrentManagedThreadId
                                            Console.WriteLine("Hello from Task #{0} created at {1} running on thread #{2}.",
                                                            mydata.Name, mydata.CreationTime, mydata.ThreadNum)
                                        End Sub,
                New MyCustomData With {.Name = i, .CreationTime = Date.Now.Ticks}
                )
                taskArray(i).Start()
            Next

        End Sub

        '</snippet02>

        Sub TaskDemo3()
            '<snippet03>

            Dim task3 = New Task(Sub() MyLongRunningMethod(),
                                    TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
            task3.Start()
            '</snippet03>

        End Sub

        ' dummy methods
        Sub MyLongRunningMethod()
        End Sub
        Function DoComputation1() As Double
            Return 1.0
        End Function
        Function DoComputation2() As Double
            DoComputation2 = 1.0
        End Function
        Function DoComputation3() As Double
            DoComputation3 = 1.0
        End Function
        Function GetFileData() As Byte()
            Dim filedata As Byte() = {10}
            Return filedata
        End Function
        Function Analyze(ByVal input() As Byte) As Double()
            Dim result As Double() = {1.0}
            Return result
        End Function
        Function Summarize(ByVal d As Double) As String
            Return "Looks good."
        End Function

        Sub MethodA()
        End Sub
        Sub MethodB()
        End Sub

        Sub MethodC()
        End Sub

        Sub MoreSimple()

            '<snippet04>
            Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation1()),
                Task(Of Double).Factory.StartNew(Function() DoComputation2()),
                Task(Of Double).Factory.StartNew(Function() DoComputation3())}


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

            '<snippet05>
            Dim getData As Task(Of Byte()) = 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 As Task(Of Double)) Summarize(y.Result))

            getData.Start()

            IO.File.WriteAllText("C:\reportFolder\report.txt", reportData.Result)

            '</snippet05>


            '<snippet06>
            Dim tasks() =
            {
                Task.Factory.StartNew(Sub() MethodA()),
                Task.Factory.StartNew(Sub() MethodB()),
                Task.Factory.StartNew(Sub() MethodC())
            }

            ' Block until all tasks complete.
            Task.WaitAll(tasks)

            ' Continue on this thread...
            '</snippet06>

        End Sub

        Shared Sub ChildTaskSnippets()

            '<snippet07>
            Dim outer = Task.Factory.StartNew(Sub()
                                                  Console.WriteLine("Outer task beginning.")
                                                  Dim child = Task.Factory.StartNew(Sub()
                                                                                        Thread.SpinWait(5000000)
                                                                                        Console.WriteLine("Detached task completed.")
                                                                                    End Sub)
                                              End Sub)
            outer.Wait()
            Console.WriteLine("Outer task completed.")
            ' The example displays the following output:
            '     Outer task beginning.
            '     Outer task completed.
            '    Detached child completed.
            '</snippet07>

            '<snippet08>
            Dim parent = Task.Factory.StartNew(Sub()
                                                   Console.WriteLine("Parent task beginning.")
                                                   Dim child = Task.Factory.StartNew(Sub()
                                                                                         Thread.SpinWait(5000000)
                                                                                         Console.WriteLine("Attached task completed.")
                                                                                     End Sub,
                                                        TaskCreationOptions.AttachedToParent)

                                               End Sub)
            outer.Wait()
            Console.WriteLine("Parent task completed.")

            ' Output:
            '     Parent task beginning.
            '     Attached task completed.
            '     Parent task completed.
            '</snippet08>



            Dim t = Task(Of Integer).Factory.StartNew(Function() 1)


        End Sub


    End Class
End Namespace

Creating child tasks

When user code that's running in a task creates a task with the System.Threading.Tasks.TaskCreationOptions.AttachedToParent option, the new task is known as an attached child task of the parent task. You can use the System.Threading.Tasks.TaskCreationOptions.AttachedToParent option to express structured task parallelism because the parent task implicitly waits for all attached child tasks to finish. The following example shows a parent task that creates 10 attached child tasks. The example calls the System.Threading.Tasks.Task.Wait method to wait for the parent task to finish. It doesn't have to explicitly wait for the attached child tasks to complete.

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/child1.cs8

csharp
// <Snippet8>
using System;
using System.Threading;
using System.Threading.Tasks;

public class Child
{
   public static void Main()
   {
      var parent = Task.Factory.StartNew(() => {
                      Console.WriteLine("Parent task beginning.");
                      for (int ctr = 0; ctr < 10; ctr++) {
                         int taskNo = ctr;
                         Task.Factory.StartNew((x) => {
                                                  Thread.SpinWait(5000000);
                                                  Console.WriteLine($"Attached child #{x} completed.");
                                               },
                                               taskNo, TaskCreationOptions.AttachedToParent);
                      }
                   });

      parent.Wait();
      Console.WriteLine("Parent task completed.");
   }
}
// The example displays output like the following:
//       Parent task beginning.
//       Attached child #9 completed.
//       Attached child #0 completed.
//       Attached child #8 completed.
//       Attached child #1 completed.
//       Attached child #7 completed.
//       Attached child #2 completed.
//       Attached child #6 completed.
//       Attached child #3 completed.
//       Attached child #5 completed.
//       Attached child #4 completed.
//       Parent task completed.
// </Snippet8>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/child1.vb8

vb
' Visual Basic .NET Document
Option Strict On

' <Snippet8>
Imports System.Threading

Namespace Child
    Module Example
        Public Sub Main()
            Dim parent = Task.Factory.StartNew(Sub()
                                                   Console.WriteLine("Parent task beginning.")
                                                   For ctr As Integer = 0 To 9
                                                       Dim taskNo As Integer = ctr
                                                       Task.Factory.StartNew(Sub(x)
                                                                                 Thread.SpinWait(5000000)
                                                                                 Console.WriteLine("Attached child #{0} completed.",
                                                                                                 x)
                                                                             End Sub,
                                                       taskNo, TaskCreationOptions.AttachedToParent)
                                                   Next
                                               End Sub)
            parent.Wait()
            Console.WriteLine("Parent task completed.")
        End Sub
    End Module
    ' The example displays output like the following:
    '       Parent task beginning.
    '       Attached child #9 completed.
    '       Attached child #0 completed.
    '       Attached child #8 completed.
    '       Attached child #1 completed.
    '       Attached child #7 completed.
    '       Attached child #2 completed.
    '       Attached child #6 completed.
    '       Attached child #3 completed.
    '       Attached child #5 completed.
    '       Attached child #4 completed.
    '       Parent task completed.
End Namespace
' </Snippet8>

A parent task can use the System.Threading.Tasks.TaskCreationOptions.DenyChildAttach option to prevent other tasks from attaching to the parent task. For more information, see Attached and Detached Child Tasks.

Waiting for tasks to finish

The System.Threading.Tasks.Task and System.Threading.Tasks.Task types provide several overloads of the System.Threading.Tasks.Task.Wait methods that enable you to wait for a task to finish. In addition, overloads of the static System.Threading.Tasks.Task.WaitAll and System.Threading.Tasks.Task.WaitAny methods let you wait for any or all of an array of tasks to finish.

Typically, you would wait for a task for one of these reasons:

  • The main thread depends on the final result computed by a task.

  • You have to handle exceptions that might be thrown from the task.

  • The application might terminate before all tasks have completed execution. For example, console applications will terminate after all synchronous code in Main (the application entry point) has executed.

The following example shows the basic pattern that doesn't involve exception handling:

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_taskintro/cs/taskintro.cs06

csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TPL_Intro
{
    class Program
    {
        static void Main(string[] args)
        {
            SimpleTaskCreation();
            ChildTaskSnippets();
            TaskDemo2();
            Console.ReadKey();
        }

        static void SimpleTaskCreation()
        {
            //<snippet01>
            // Create a task and supply a user delegate by using a lambda expression.
            var taskA = new Task(() => Console.WriteLine("Hello from taskA."));

            // Start the task.
            taskA.Start();

            // Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.");
            // Message from taskA should follow.

            /* Output:
             * Hello from the calling thread.
             * Hello from taskA.
             */

            //</snippet01>
        }

        static void SimpleStartNew()
        {
            //<snippet09>
            // Create and start the task in one operation.
            var taskA = Task.Factory.StartNew(() => Console.WriteLine("Hello from taskA."));

            // Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.");
            //</snippet09>
        }

        //<snippet02>

       class MyCustomData
       {
        public long CreationTime;
        public int Name;
        public int ThreadNum;
        }

    static void TaskDemo2()
    {
        // Create the task object by using an Action(Of Object) to pass in custom data
        // in the Task constructor. This is useful when you need to capture outer variables
        // from within a loop. As an experiment, try modifying this code to
        // capture i directly in the lambda, and compare results.
        Task[] taskArray = new Task[10];

        for(int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = new Task((obj) =>
                {
                                        MyCustomData mydata = (MyCustomData) obj;
                                        mydata.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                        Console.WriteLine($"Hello from Task #{mydata.Name} created at {mydata.CreationTime} running on thread #{mydata.ThreadNum}.");
                },
            new MyCustomData () {Name = i, CreationTime = DateTime.Now.Ticks}
            );
            taskArray[i].Start();
        }
    }

        //</snippet02>

        static void TaskDemo2A()
        {
            Task.Factory.StartNew(state =>
            {
                dynamic data = state;
                Console.WriteLine(data.Name);
                Console.WriteLine(data.Date);
            }, new { Name = "taskB", Date = DateTime.Today });
        }

        static void TaskDemo3()
        {
            //<snippet03>
            var task3 = new Task(() => MyLongRunningMethod(),
                                TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
            task3.Start();
            //</snippet03>
        }

        // dummy methods
        static void MyLongRunningMethod() { }
        static double DoComputation1() { return 1.0; }
        static double DoComputation2() { return 1.0; }
        static double DoComputation3() { return 1.0; }
        static byte[] GetFileData() { return new byte[100]; }
        static double[] Analyze(byte[] input) { return new double[100]; }
        static string Summarize(double[] d) { return "looks good"; }
        static void MethodA() { }
        static void MethodB() { }
        static void MethodC() { }

        static void MoreSimple()
        {

            //<snippet04>
            Task<double>[] taskArray = new Task<double>[]
               {
                   Task<double>.Factory.StartNew(() => DoComputation1()),

                   // May be written more conveniently like this:
                   Task.Factory.StartNew(() => DoComputation2()),
                   Task.Factory.StartNew(() => DoComputation3())
               };

            double[] results = new double[taskArray.Length];
            for (int i = 0; i < taskArray.Length; i++)
                results[i] = taskArray[i].Result;
            //</snippet04>

            //<snippet05>
            Task<byte[]> getData = new Task<byte[]>(() => GetFileData());
            Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result));
            Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result));

            getData.Start();

            //or...
            Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData())
                                        .ContinueWith((x) => Analyze(x.Result))
                                        .ContinueWith((y) => Summarize(y.Result));

            System.IO.File.WriteAllText(@"C:\reportFolder\report.txt", reportData.Result);

            //</snippet05>

            //<snippet06>
            Task[] tasks = new Task[3]
            {
                Task.Factory.StartNew(() => MethodA()),
                Task.Factory.StartNew(() => MethodB()),
                Task.Factory.StartNew(() => MethodC())
            };

            //Block until all tasks complete.
            Task.WaitAll(tasks);

            // Continue on this thread...
            //</snippet06>
        }

        static void ChildTaskSnippets()
        {
            //<snippet07>
            var outer = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Outer task beginning.");

                var child = Task.Factory.StartNew(() =>
                {
                    Thread.SpinWait(5000000);
                    Console.WriteLine("Detached task completed.");
                });
            });

            outer.Wait();
            Console.WriteLine("Outer task completed.");
            // The example displays the following output:
            //    Outer task beginning.
            //    Outer task completed.
            //    Detached task completed.
            //</snippet07>

            //<snippet08>
            var parent = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Parent task beginning.");

                var child = Task.Factory.StartNew(() =>
                {
                    Thread.SpinWait(5000000);
                    Console.WriteLine("Attached task completed.");
                }, TaskCreationOptions.AttachedToParent);
            });

            parent.Wait();
            Console.WriteLine("Parent task completed.");

            /* Output:
                Parent task beginning.
                Attached task completed.
                Parent task completed.
             */
            //</snippet08>

            var t = Task<int>.Factory.StartNew(() => 1);
        }
    }
}

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_taskintro/vb/tpl_intro.vb06

vb
Imports System.Threading

Namespace Introduction
    Class Program

        Public Shared Sub Main()
            SimpleTaskCreation()
            ChildTaskSnippets()
            TaskDemo2()
            Console.ReadKey()
        End Sub


        Shared Sub SimpleTaskCreation()
            '<snippet01>
            ' Create a task and supply a user delegate by using a lambda expression.
            Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))

            ' Start the task.
            taskA.Start()

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.")

            ' Output:
            ' Hello from the calling thread.
            ' Hello from taskA.

            '</snippet01>
        End Sub

        Sub SimpleStartNew()

            '<snippet09>
            ' Better: Create and start the task in one operation.
            Dim taskA = Task.Factory.StartNew(Sub() Console.WriteLine("Hello from taskA."))

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from the calling thread.")
            '</snippet09>
        End Sub


        '<snippet02>

        Class MyCustomData

            Public CreationTime As Long
            Public Name As Integer
            Public ThreadNum As Integer
        End Class

        Shared Sub TaskDemo2()
            ' Create the task object by using an Action(Of Object) to pass in custom data
            ' in the Task constructor. This is useful when you need to capture outer variables
            ' from within a loop.
            ' As an experiment, try modifying this code to capture i directly in the lambda,
            ' and compare results.
            Dim taskArray() As Task
            ReDim taskArray(10)
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = New Task(Sub(obj As Object)
                                            Dim mydata = CType(obj, MyCustomData)
                                            mydata.ThreadNum = Environment.CurrentManagedThreadId
                                            Console.WriteLine("Hello from Task #{0} created at {1} running on thread #{2}.",
                                                            mydata.Name, mydata.CreationTime, mydata.ThreadNum)
                                        End Sub,
                New MyCustomData With {.Name = i, .CreationTime = Date.Now.Ticks}
                )
                taskArray(i).Start()
            Next

        End Sub

        '</snippet02>

        Sub TaskDemo3()
            '<snippet03>

            Dim task3 = New Task(Sub() MyLongRunningMethod(),
                                    TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
            task3.Start()
            '</snippet03>

        End Sub

        ' dummy methods
        Sub MyLongRunningMethod()
        End Sub
        Function DoComputation1() As Double
            Return 1.0
        End Function
        Function DoComputation2() As Double
            DoComputation2 = 1.0
        End Function
        Function DoComputation3() As Double
            DoComputation3 = 1.0
        End Function
        Function GetFileData() As Byte()
            Dim filedata As Byte() = {10}
            Return filedata
        End Function
        Function Analyze(ByVal input() As Byte) As Double()
            Dim result As Double() = {1.0}
            Return result
        End Function
        Function Summarize(ByVal d As Double) As String
            Return "Looks good."
        End Function

        Sub MethodA()
        End Sub
        Sub MethodB()
        End Sub

        Sub MethodC()
        End Sub

        Sub MoreSimple()

            '<snippet04>
            Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation1()),
                Task(Of Double).Factory.StartNew(Function() DoComputation2()),
                Task(Of Double).Factory.StartNew(Function() DoComputation3())}


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

            '<snippet05>
            Dim getData As Task(Of Byte()) = 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 As Task(Of Double)) Summarize(y.Result))

            getData.Start()

            IO.File.WriteAllText("C:\reportFolder\report.txt", reportData.Result)

            '</snippet05>


            '<snippet06>
            Dim tasks() =
            {
                Task.Factory.StartNew(Sub() MethodA()),
                Task.Factory.StartNew(Sub() MethodB()),
                Task.Factory.StartNew(Sub() MethodC())
            }

            ' Block until all tasks complete.
            Task.WaitAll(tasks)

            ' Continue on this thread...
            '</snippet06>

        End Sub

        Shared Sub ChildTaskSnippets()

            '<snippet07>
            Dim outer = Task.Factory.StartNew(Sub()
                                                  Console.WriteLine("Outer task beginning.")
                                                  Dim child = Task.Factory.StartNew(Sub()
                                                                                        Thread.SpinWait(5000000)
                                                                                        Console.WriteLine("Detached task completed.")
                                                                                    End Sub)
                                              End Sub)
            outer.Wait()
            Console.WriteLine("Outer task completed.")
            ' The example displays the following output:
            '     Outer task beginning.
            '     Outer task completed.
            '    Detached child completed.
            '</snippet07>

            '<snippet08>
            Dim parent = Task.Factory.StartNew(Sub()
                                                   Console.WriteLine("Parent task beginning.")
                                                   Dim child = Task.Factory.StartNew(Sub()
                                                                                         Thread.SpinWait(5000000)
                                                                                         Console.WriteLine("Attached task completed.")
                                                                                     End Sub,
                                                        TaskCreationOptions.AttachedToParent)

                                               End Sub)
            outer.Wait()
            Console.WriteLine("Parent task completed.")

            ' Output:
            '     Parent task beginning.
            '     Attached task completed.
            '     Parent task completed.
            '</snippet08>



            Dim t = Task(Of Integer).Factory.StartNew(Function() 1)


        End Sub


    End Class
End Namespace

For an example that shows exception handling, see Exception Handling.

Some overloads let you specify a time-out, and others take an additional System.Threading.CancellationToken as an input parameter so that the wait itself can be canceled either programmatically or in response to user input.

When you wait for a task, you implicitly wait for all children of that task that were created by using the System.Threading.Tasks.TaskCreationOptions.AttachedToParent option. System.Threading.Tasks.Task.Wait returns immediately if the task has already completed. A System.Threading.Tasks.Task.Wait method throws any exceptions raised by a task, even if the System.Threading.Tasks.Task.Wait method was called after the task completed.

Composing tasks

The System.Threading.Tasks.Task and System.Threading.Tasks.Task classes provide several methods to help you compose multiple tasks. These methods implement common patterns and make better use of the asynchronous language features that are provided by C#, Visual Basic, and F#. This section describes the System.Threading.Tasks.Task.WhenAll, System.Threading.Tasks.Task.WhenAny, System.Threading.Tasks.Task.Delay, and System.Threading.Tasks.Task.FromResult methods.

Task.WhenAll

The System.Threading.Tasks.Task.WhenAll method asynchronously waits for multiple System.Threading.Tasks.Task or System.Threading.Tasks.Task objects to finish. It provides overloaded versions that enable you to wait for non-uniform sets of tasks. For example, you can wait for multiple System.Threading.Tasks.Task and System.Threading.Tasks.Task objects to complete from one method call.

Task.WhenAny

The System.Threading.Tasks.Task.WhenAny method asynchronously waits for one of multiple System.Threading.Tasks.Task or System.Threading.Tasks.Task objects to finish. As in the System.Threading.Tasks.Task.WhenAll method, this method provides overloaded versions that enable you to wait for non-uniform sets of tasks. The System.Threading.Tasks.Task.WhenAny method is especially useful in the following scenarios:

Task.Delay

The System.Threading.Tasks.Task.Delay method produces a System.Threading.Tasks.Task object that finishes after the specified time. You can use this method to build loops that poll for data, to specify time-outs, to delay the handling of user input, and so on.

Task(T).FromResult

By using the System.Threading.Tasks.Task.FromResult method, you can create a System.Threading.Tasks.Task object that holds a pre-computed result. This method is useful when you perform an asynchronous operation that returns a System.Threading.Tasks.Task object, and the result of that System.Threading.Tasks.Task object is already computed. For an example that uses System.Threading.Tasks.Task.FromResult to retrieve the results of asynchronous download operations that are held in a cache, see How to: Create Pre-Computed Tasks.

Handling exceptions in tasks

When a task throws one or more exceptions, the exceptions are wrapped in an System.AggregateException exception. That exception is propagated back to the thread that joins with the task. Typically, it's the thread waiting for the task to finish or the thread accessing the System.Threading.Tasks.Task.Result property. This behavior enforces the .NET Framework policy that all unhandled exceptions by default should terminate the process. The calling code can handle the exceptions by using any of the following in a try/catch block:

The joining thread can also handle exceptions by accessing the System.Threading.Tasks.Task.Exception property before the task is garbage-collected. By accessing this property, you prevent the unhandled exception from triggering the exception propagation behavior that terminates the process when the object is finalized.

For more information about exceptions and tasks, see Exception Handling.

Canceling tasks

The System.Threading.Tasks.Task class supports cooperative cancellation and is fully integrated with the System.Threading.CancellationTokenSource and System.Threading.CancellationToken classes, which were introduced in the .NET Framework 4. Many of the constructors in the System.Threading.Tasks.Task class take a System.Threading.CancellationToken object as an input parameter. Many of the System.Threading.Tasks.TaskFactory.StartNew and System.Threading.Tasks.Task.Run overloads also include a System.Threading.CancellationToken parameter.

You can create the token and issue the cancellation request at some later time, by using the System.Threading.CancellationTokenSource class. Pass the token to the System.Threading.Tasks.Task as an argument, and also reference the same token in your user delegate, which does the work of responding to a cancellation request.

For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

The TaskFactory class

The System.Threading.Tasks.TaskFactory class provides static methods that encapsulate common patterns for creating and starting tasks and continuation tasks.

The default System.Threading.Tasks.TaskFactory can be accessed as a static property on the System.Threading.Tasks.Task class or System.Threading.Tasks.Task class. You can also instantiate a System.Threading.Tasks.TaskFactory directly and specify various options that include a System.Threading.CancellationToken, a System.Threading.Tasks.TaskCreationOptions option, a System.Threading.Tasks.TaskContinuationOptions option, or a System.Threading.Tasks.TaskScheduler. Whatever options are specified when you create the task factory are applied to all tasks that it creates unless the System.Threading.Tasks.Task is created by using the System.Threading.Tasks.TaskCreationOptions enumeration, in which case the task's options override those of the task factory.

Tasks without delegates

In some cases, you might want to use a System.Threading.Tasks.Task to encapsulate some asynchronous operation that's performed by an external component instead of your user delegate. If the operation is based on the Asynchronous Programming Model Begin/End pattern, you can use the System.Threading.Tasks.TaskFactory.FromAsync methods. If that's not the case, you can use the System.Threading.Tasks.TaskCompletionSource object to wrap the operation in a task and thereby gain some of the benefits of System.Threading.Tasks.Task programmability. For example, support for exception propagation and continuations. For more information, see System.Threading.Tasks.TaskCompletionSource.

Custom schedulers

Most application or library developers don't care which processor the task runs on, how it synchronizes its work with other tasks, or how it's scheduled on the System.Threading.ThreadPool. They only require that it execute as efficiently as possible on the host computer. If you require more fine-grained control over the scheduling details, the TPL lets you configure some settings on the default task scheduler, and even lets you supply a custom scheduler. For more information, see System.Threading.Tasks.TaskScheduler.

The TPL has several new public types that are useful in parallel and sequential scenarios. These include several thread-safe, fast, and scalable collection classes in the System.Collections.Concurrent namespace and several new synchronization types. For example, System.Threading.Semaphore and System.Threading.ManualResetEventSlim, which are more efficient than their predecessors for specific kinds of workloads. Other new types in the .NET Framework 4, for example, System.Threading.Barrier and System.Threading.SpinLock, provide functionality that wasn't available in earlier releases. For more information, see Data Structures for Parallel Programming.

Custom task types

We recommend that you don't inherit from System.Threading.Tasks.Task or System.Threading.Tasks.Task. Instead, we recommend that you use the System.Threading.Tasks.Task.AsyncState property to associate additional data or state with a System.Threading.Tasks.Task or System.Threading.Tasks.Task object. You can also use extension methods to extend the functionality of the System.Threading.Tasks.Task and System.Threading.Tasks.Task classes. For more information about extension methods, see Extension Methods and Extension Methods.

If you must inherit from System.Threading.Tasks.Task or System.Threading.Tasks.Task, you can't use System.Threading.Tasks.Task.Run or the System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory, or System.Threading.Tasks.TaskCompletionSource classes to create instances of your custom task type. You can't use them because these classes create only System.Threading.Tasks.Task and System.Threading.Tasks.Task objects. In addition, you can't use the task continuation mechanisms that are provided by System.Threading.Tasks.Task, System.Threading.Tasks.Task, System.Threading.Tasks.TaskFactory, and System.Threading.Tasks.TaskFactory to create instances of your custom task type. You can't use them because these classes also create only System.Threading.Tasks.Task and System.Threading.Tasks.Task objects.

Title Description
Chaining Tasks by Using Continuation Tasks Describes how continuations work.
Attached and Detached Child Tasks Describes the difference between attached and detached child tasks.
Task Cancellation Describes the cancellation support that's built into the System.Threading.Tasks.Task object.
Exception Handling Describes how exceptions on concurrent threads are handled.
How to: Use Parallel.Invoke to Execute Parallel Operations Describes how to use System.Threading.Tasks.Parallel.Invoke.
How to: Return a Value from a Task Describes how to return values from tasks.
How to: Cancel a Task and Its Children Describes how to cancel tasks.
How to: Create Pre-Computed Tasks Describes how to use the System.Threading.Tasks.Task.FromResult method to retrieve the results of asynchronous download operations that are held in a cache.
How to: Traverse a Binary Tree with Parallel Tasks Describes how to use tasks to traverse a binary tree.
How to: Unwrap a Nested Task Demonstrates how to use the System.Threading.Tasks.TaskExtensions.Unwrap extension method.
Data Parallelism Describes how to use System.Threading.Tasks.Parallel.For and System.Threading.Tasks.Parallel.ForEach to create parallel loops over data.
Parallel Programming Top-level node for .NET Framework parallel programming.

See also