Asynchronní programování založené na úlohách

Knihovna TPL (Task Parallel Library) je založená na konceptu úlohy, která představuje asynchronní operaci. Úloha se některým způsobem podobá vláknu nebo ThreadPool pracovní položce, ale na vyšší úrovni abstrakce. Termín paralelismus úkolu odkazuje na jeden nebo více nezávislých úloh spuštěných souběžně. Úlohy poskytují dvě hlavní výhody:

  • Větší škálovatelnost a efektivnější využívání systémových prostředků.

    Na pozadí jsou úkoly zařazeny do ThreadPoolfronty , která byla vylepšena pomocí algoritmů, které určují a upravují počet vláken. Tyto algoritmy poskytují vyrovnávání zatížení pro maximalizaci propustnosti. Díky tomuto procesu jsou úlohy poměrně jednoduché a můžete je vytvořit, abyste umožnili jemně odstupňovaný paralelismus.

  • Více programového ovládání než je k dispozici s vláknem nebo pracovní položkou.

    Úlohy a architektura kolem nich vytvořená poskytují bohatou sadu rozhraní (API), která podporují čekání, zrušení, pokračování, robustní zpracování výjimek, podrobný stav, vlastní plánování a další.

Z obou důvodů je TPL upřednostňovaným rozhraním API pro psaní vícevláknového, asynchronního a paralelního kódu v .NET.

Implicitní vytváření a spouštění úloh

Tato Parallel.Invoke metoda poskytuje pohodlný způsob, jak souběžně spustit libovolný počet libovolných příkazů. Stačí předat delegáta Action pro každou položku práce. Nejsnadnější způsob, jak vytvořit tyto delegáty, je použití lambda výrazů. Lambda výraz může buďto volat pojmenovanou metodu, nebo poskytnout vložený kód. Následující příklad ukazuje základní Invoke volání, které vytvoří a spustí dvě úlohy, které běží souběžně. První úkol je reprezentován výrazem lambda, který volá metodu s názvem DoSomeWork, a druhý úkol je reprezentován výrazem lambda, který volá metodu s názvem DoSomeOtherWork.

Poznámka:

Tato dokumentace používá k definování delegátů v TPL lambda výrazy. Pokud výrazy lambda v jazyce C# nebo Visual Basic neznáte, podívejte se na výrazy lambda v PLINQ a TPL.

Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())

Poznámka:

Počet Task instancí vytvořených na pozadí Invoke nemusí nutně odpovídat počtu poskytnutých delegátů. TPL může využívat různé optimalizace, zejména u velkého počtu delegátů.

Další informace naleznete v tématu Postupy: Použití Parallel.Invoke ke spouštění paralelních operací.

Pokud chcete mít větší kontrolu nad prováděním úkolů nebo vrácením hodnoty z úkolu, musíte Task s objekty pracovat explicitněji.

Explicitní vytváření a spouštění úloh

Úkol, který nevrací hodnotu, je reprezentován System.Threading.Tasks.Task třídou. Úkol, který vrací hodnotu, je reprezentován System.Threading.Tasks.Task<TResult> třídou, která dědí z Task. Objekt úlohy zpracovává podrobnosti infrastruktury a poskytuje metody a vlastnosti, které jsou přístupné z volajícího vlákna po celou dobu trvání úlohy. K vlastnosti úkolu můžete přistupovat Status kdykoliv, abyste zjistili, jestli byl spuštěn, spuštěn do dokončení, byl zrušen nebo vyvolá výjimku. Stav je reprezentován výčtem TaskStatus .

Když je vytvořena úloha, je jí zadán uživatelský delegát, jenž provádí zapouzdření kódu, který úloha provede. Tento delegát může být vyjádřen jako pojmenovaný delegát, anonymní metoda nebo lambda výraz. Lambda výrazy mohou obsahovat volání pojmenované metody, jak ukazuje následující příklad. Příklad obsahuje volání Task.Wait metody, aby se zajistilo, že úloha dokončí provádění před ukončením aplikace v režimu konzoly.

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 '{0}'.",
                        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'.
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

Pomocí metod můžete také Task.Run vytvořit a spustit úlohu v jedné operaci. Ke správě úlohy Run používají metody výchozí plánovač úloh bez ohledu na to, který plánovač úloh je přidružen k aktuálnímu vláknu. Metody Run jsou upřednostňovaným způsobem, jak vytvářet a spouštět úkoly, pokud není potřeba mít větší kontrolu nad vytvořením a plánováním úkolu.

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 '{0}'.",
                          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'.
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

Metodu TaskFactory.StartNew můžete použít také k vytvoření a spuštění úlohy v jedné operaci. Jak je znázorněno v následujícím příkladu, můžete tuto metodu použít v těchto případech:

  • Vytváření a plánování nemusí být oddělené a vyžadujete další možnosti vytváření úkolů nebo použití konkrétního plánovače.

  • Do úlohy, kterou můžete načíst, Task.AsyncState musíte předat další stav.

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

Task a Task<TResult> každá zpřístupňuje statickou Factory vlastnost, která vrací výchozí instanci TaskFactory, takže můžete volat metodu jako Task.Factory.StartNew(). Také v následujícím příkladu, protože úkoly jsou typu System.Threading.Tasks.Task<TResult>, mají každou veřejnou Task<TResult>.Result vlastnost, která obsahuje výsledek výpočtu. Úlohy běží asynchronně a můžou být dokončeny v libovolném pořadí. Result Pokud je vlastnost přístupná před dokončením výpočtu, vlastnost blokuje volající vlákno, dokud není k dispozici hodnota.

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("{0:N1}", sum);
   }

   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

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

Další informace naleznete v tématu Postupy: Vrácení hodnoty z úkolu.

Při použití lambda výrazu k vytvoření delegáta máte přístup ke všem proměnným, které jsou v daném okamžiku viditelné ve zdrojovém kódu. V některých případech, zejména v rámci smyčky, však lambda nezachytí proměnnou podle očekávání. Zachycuje pouze odkaz na proměnnou, nikoli hodnotu, protože se ztlumí po každé iteraci. Následující příklad ukazuje tento problém. Předá čítač smyčky výrazu lambda, který vytvoří instanci objektu CustomData a použije čítač smyčky jako identifikátor objektu. Jak ukazuje výstup z příkladu, každý CustomData objekt má identický identifikátor.

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 #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, 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.
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

K hodnotě v každé iteraci můžete přistupovat zadáním objektu stavu úlohy pomocí jeho konstruktoru. Následující příklad upraví předchozí příklad pomocí čítače smyčky při vytváření objektu CustomData , který se následně předá výrazu lambda. Jak ukazuje výstup z příkladu, každý CustomData objekt má nyní jedinečný identifikátor založený na hodnotě čítače smyčky v době vytvoření instance objektu.

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 #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, 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.
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

Tento stav se předává jako argument delegátu úkolu a lze k němu přistupovat z objektu Task.AsyncState úkolu pomocí vlastnosti. Následující příklad je podobný předchozímu příkladu. Používá AsyncState vlastnost k zobrazení informací o CustomData objektech předaných výrazu lambda.

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

ID úlohy

Každý úkol obdrží celočíselné ID, které ho jednoznačně identifikuje v doméně aplikace a je možné k němu přistupovat pomocí Task.Id vlastnosti. ID je užitečné pro zobrazení informací o úlohách v oknech paralelních zásobníků a úloh sady Visual Studio. ID se vytvoří laziálně, což znamená, že se nevytvořilo, dokud se o to nepožádá. Proto může mít úkol při každém spuštění programu jiné ID. Další informace o zobrazení ID úloh v ladicím programu naleznete v tématu Použití okna Úlohy a Použití okna Paralelní zásobníky.

Možnosti vytváření úkolů

Většina rozhraní API, která vytvářejí úlohy, poskytuje přetížení, která přijímají TaskCreationOptions parametr. Zadáním jedné nebo více těchto možností sdělíte plánovači úloh, jak naplánovat úlohu ve fondu vláken. Možnosti se můžou kombinovat pomocí bitové operace OR .

Následující příklad ukazuje úlohu s možnostmi LongRunning a PreferFairness možnostmi:

var task3 = new Task(() => MyLongRunningMethod(),
                    TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();

Dim task3 = New Task(Sub() MyLongRunningMethod(),
                        TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()

Úlohy, vlákna a jazyková verze

Každé vlákno má přidruženou jazykovou verzi a jazykovou verzi uživatelského rozhraní, které jsou definovány vlastnostmi Thread.CurrentCulture a Thread.CurrentUICulture vlastnostmi. Jazyková verze vlákna se používá v operacích, jako je formátování, analýza, řazení a operace porovnání řetězců. Jazyková verze uživatelského rozhraní vlákna se používá ve vyhledávání prostředků.

Systémová jazyková verze definuje výchozí jazykovou verzi a jazykovou verzi uživatelského rozhraní vlákna. Pomocí vlastností CultureInfo.DefaultThreadCurrentCultureCultureInfo.DefaultThreadCurrentUICulture ale můžete zadat výchozí jazykovou verzi pro všechna vlákna v doméně aplikace. Pokud explicitně nastavíte jazykovou verzi vlákna a spustíte nové vlákno, nové vlákno nezdědí jazykovou verzi volajícího vlákna; místo toho je jeho jazyková verze výchozí systémovou jazykovou verzí. V programování založeném na úlohách však úlohy používají jazykovou verzi volajícího vlákna, i když úloha běží asynchronně na jiném vlákně.

Následující příklad obsahuje jednoduchý obrázek. Změní aktuální jazykovou verzi aplikace na francouzštinu (Francie). Pokud je francouzština (Francie) již aktuální jazykovou verzí, změní se na angličtinu (USA). Potom vyvolá delegáta s názvem formatDelegate , který vrátí některá čísla formátovaná jako hodnoty měny v nové jazykové verzi. Bez ohledu na to, zda je delegát vyvolán úlohou synchronně nebo asynchronně, úloha používá jazykovou verzi volajícího vlákna.

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 {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")
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
       else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

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

       // 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 €
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 €

Poznámka:

Ve verzích rozhraní .NET Framework starších než .NET Framework 4.6 je jazyková verze úlohy určena jazykovou verzí vlákna, na kterém běží, nikoli jazykovou verzí volajícího vlákna. U asynchronních úloh se jazyková verze používaná úlohou může lišit od jazykové verze volajícího vlákna.

Další informace o asynchronních úlohách a jazykové verzi najdete v části Jazykové verze a asynchronní operace založené na úlohách v CultureInfo článku.

Vytváření pokračování úkolů

Metody Task.ContinueWith umožňují Task<TResult>.ContinueWith určit úkol, který se má spustit, když se dokončí úkol stecedent . Delegát úkolu pokračování je předán odkaz na úkol stecedent, aby mohl prozkoumat stav úkolu stecedent. A načtením hodnoty Task<TResult>.Result vlastnosti můžete použít výstup antecedent jako vstup pro pokračování.

V následujícím příkladu getData je úloha spuštěna voláním TaskFactory.StartNew<TResult>(Func<TResult>) metody. Úkol processData se spustí automaticky po getData dokončení a displayData po dokončení se spustí processData . getData vytvoří celočíselné pole, které je přístupné pro processData úkol prostřednictvím getData vlastnosti úkolu Task<TResult>.Result . Úloha processData zpracuje pole a vrátí výsledek, jehož typ je odvozen z návratového typu výrazu lambda předaného metodě Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) . Úkol displayData se spustí automaticky po processData dokončení a Tuple<T1,T2,T3> objekt vrácený výrazem processData lambda je přístupný úkolu displayData prostřednictvím processData vlastnosti úkolu Task<TResult>.Result . Úkol displayData převezme výsledek processData úkolu. Vytvoří výsledek, jehož typ je odvozen podobným způsobem a který je zpřístupněn programu ve Result vlastnosti.

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

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

Protože Task.ContinueWith je metoda instance, můžete zřetězení volání metody namísto vytvoření instance Task<TResult> objektu pro každou úlohu stecedent. Následující příklad je funkčně identický s předchozím, s tím rozdílem, že je zřetězí volání Task.ContinueWith metody. Objekt Task<TResult> vrácený řetězem volání metody je posledním pokračováním úkolu.

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

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

ContinueWhenAny Metody ContinueWhenAll umožňují pokračovat z více úkolů.

Další informace naleznete v tématu Zřetězování úkolů pomocí úloh pokračování.

Vytváření odpojených podřízených úkolů

Když uživatelský kód spuštěný v úkolu vytvoří nový úkol a nezadá AttachedToParent možnost, nový úkol se nijak jinak nesynchronuje s nadřazeným úkolem. Tento typ nesynchronní úlohy se nazývá odpojený vnořený úkol nebo odpojený podřízený úkol. Následující příklad ukazuje úlohu, která vytvoří jednu odpojenou podřízenou úlohu:

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

Poznámka:

Nadřazený úkol nečeká na dokončení odpojeného podřízeného úkolu.

Vytváření podřízených úkolů

Když uživatelský kód spuštěný v úkolu vytvoří úkol s AttachedToParent možností, nový úkol se označuje jako připojený podřízený úkol nadřazeného úkolu. Můžete použít AttachedToParent možnost vyjádřit strukturovaný paralelismus úkolu, protože nadřazený úkol implicitně čeká na dokončení všech připojených podřízených úkolů. Následující příklad ukazuje nadřazený úkol, který vytvoří 10 připojených podřízených úkolů. Příklad volá metodu Task.Wait , která čeká na dokončení nadřazené úlohy. Nemusí explicitně čekat na dokončení připojených podřízených úkolů.

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 #{0} completed.",
                                                                    x);
                                               },
                                               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.
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

Nadřazený úkol může použít TaskCreationOptions.DenyChildAttach možnost, aby se ostatní úkoly nepřipošly k nadřazené úloze. Další informace naleznete v tématu Připojené a odpojené podřízené úkoly.

Čekání na dokončení úkolů

System.Threading.Tasks.Task<TResult> Typy System.Threading.Tasks.Task poskytují několik přetížení Task.Wait metod, které umožňují počkat na dokončení úkolu. Kromě toho přetížení statických Task.WaitAll a Task.WaitAny metod umožňují čekat na dokončení libovolného nebo všech polí úkolů.

Obvykle se na úlohu čeká z některého z těchto důvodů:

  • Hlavní vlákno závisí na konečném výsledku vypočítaném úlohou.

  • Je potřeba zpracovat výjimky, které mohou být vyvolány z úlohy.

  • Aplikace se může ukončit před dokončením provádění všech úkolů. Například konzolové aplikace se ukončí poté, co se spustí veškerý synchronní kód ( Main vstupní bod aplikace).

Následující příklad ukazuje základní vzor, který nezahrnuje zpracování výjimek:

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

Příklad znázorňující zpracování výjimek najdete v tématu Zpracování výjimek.

Některá přetížení umožňují určit časový limit a jiné jako vstupní parametr berou další CancellationToken jako vstupní parametr, aby samotné čekání bylo možné zrušit buď programově, nebo v reakci na uživatelský vstup.

Když počkáte na úkol, implicitně čekáte na všechny podřízené položky tohoto úkolu vytvořené pomocí TaskCreationOptions.AttachedToParent této možnosti. Task.Wait vrátí okamžitě, pokud úkol již byl dokončen. Metoda Task.Wait vyvolá všechny výjimky vyvolané úlohou, i když Task.Wait byla metoda volána po dokončení úkolu.

Psaní úkolů

Task<TResult> Třídy Task poskytují několik metod, které vám pomůžou vytvářet více úkolů. Tyto metody implementují běžné vzory a využívají lépe asynchronní jazykové funkce poskytované jazykem C#, Visual Basic a F#. Tato část popisuje WhenAll, WhenAny, Delaya FromResult metody.

Task.WhenAll

Metoda Task.WhenAll asynchronně čeká na dokončení více Task objektů nebo Task<TResult> objektů. Poskytuje přetížené verze, které umožňují čekání na nerovnoměrné sady úloh. Můžete například počkat na dokončení více Task objektů Task<TResult> z jednoho volání metody.

Task.WhenAny

Metoda Task.WhenAny asynchronně čeká na dokončení jednoho z více Task objektů nebo Task<TResult> objektů. Stejně jako v Task.WhenAll metodě tato metoda poskytuje přetížené verze, které umožňují čekat na ne uniformní sady úloh. Metoda WhenAny je zvlášť užitečná v následujících scénářích:

  • Redundantní operace: Zvažte algoritmus nebo operaci, kterou lze provádět mnoha způsoby. Pomocí této metody můžete WhenAny vybrat operaci, která se dokončí jako první, a potom zrušit zbývající operace.

  • Prokládání operací: Můžete spustit více operací, které musí dokončit a použít metodu WhenAny ke zpracování výsledků při dokončení každé operace. Po dokončení jedné operace můžete spustit jeden nebo více úkolů.

  • Omezené operace: Metodu WhenAny můžete použít k rozšíření předchozího scénáře omezením počtu souběžných operací.

  • Operace s vypršenou platností: Pomocí metody můžete WhenAny vybrat jeden nebo více úkolů a úkol, který se dokončí po určitém čase, například úkol vrácený metodou Delay . Metoda Delay je popsaná v následující části.

Task.Delay

Metoda Task.Delay vytvoří Task objekt, který se dokončí po zadaném čase. Tuto metodu můžete použít k sestavení smyček, které se dotazují na data, k určení časových limitů, ke zpoždění zpracování vstupu uživatele atd.

Task(T).FromResult

Pomocí Task.FromResult metody můžete vytvořit Task<TResult> objekt, který obsahuje předem vypočítaný výsledek. Tato metoda je užitečná při provádění asynchronní operace, která vrací Task<TResult> objekt, a výsledek tohoto Task<TResult> objektu je již vypočítán. Příklad, který používá FromResult k načtení výsledků asynchronních operací stahování uložených v mezipaměti, najdete v tématu Postupy: Vytváření předpočítaných úloh.

Zpracování výjimek v úlohách

Pokud úloha vyvolá jednu nebo více výjimek, jsou výjimky zabaleny do AggregateException výjimky. Tato výjimka se rozšíří zpět do vlákna, které se spojí s úlohou. Obvykle se jedná o vlákno, které čeká na dokončení úlohy, nebo vlákno, které Result přistupuje k vlastnosti. Toto chování vynucuje zásadu rozhraní .NET Framework, kterou by měly všechny neošetřené výjimky ve výchozím nastavení ukončit. Volající kód může zpracovávat výjimky pomocí některého z následujících postupů v try/catch bloku:

Spojovací vlákno může také zpracovávat výjimky tím, že přistupuje k Exception vlastnosti před uvolněním paměti úlohy. Přístupem k této vlastnosti nebude nezpracovaná výjimka spouštět chování šíření výjimky, které ukončí proces, když je objekt finalizován.

Další informace o výjimkách a úlohách najdete v tématu Zpracování výjimek.

Zrušení úkolů

Třída Task podporuje kooperativní zrušení a je plně integrovaná s třídami System.Threading.CancellationTokenSourceSystem.Threading.CancellationToken , které byly zavedeny v rozhraní .NET Framework 4. Mnoho konstruktorů ve System.Threading.Tasks.Task třídě vezme CancellationToken objekt jako vstupní parametr. Mnoho z StartNewRun přetížení zahrnuje CancellationToken také parametr.

Token můžete vytvořit a později vydat žádost o zrušení pomocí CancellationTokenSource třídy. Předání tokenu Task jako argumentu a také odkazování na stejný token v delegátu uživatele, který funguje při odpovídání na žádost o zrušení.

Další informace naleznete v tématu Zrušení a postupy úkolu: Zrušení úkolu a jeho podřízených položek.

TaskFactory – třída

Třída TaskFactory poskytuje statické metody, které zapouzdřují běžné vzory pro vytváření a spouštění úkolů a pokračování úkolů.

Výchozí možnost TaskFactory je přístupná jako statická vlastnost třídy Task nebo Task<TResult> třídy. Můžete také vytvořit TaskFactory instanci přímo a zadat různé možnosti, které zahrnují CancellationToken, TaskCreationOptions možnost, TaskContinuationOptions možnost nebo TaskScheduler. Jakékoli možnosti, které se zadají při vytváření objektu pro vytváření úloh, se použijí pro všechny úkoly, které vytvoří, pokud Task není vytvořena pomocí výčtu TaskCreationOptions , v takovém případě možnosti úkolu přepíšou možnosti objektu pro vytváření úloh.

Úkoly bez delegátů

V některých případech můžete chtít použít Task k zapouzdření některé asynchronní operace prováděné externí komponentou místo delegáta uživatele. Pokud je operace založená na vzoru Begin/End asynchronního programovacího modelu, můžete použít FromAsync metody. Pokud tomu tak není, můžete TaskCompletionSource<TResult> pomocí objektu zabalit operaci do úkolu a získat tak některé výhody Task programovatelnosti. Například podpora šíření výjimek a pokračování. Další informace najdete na webu TaskCompletionSource<TResult>.

Vlastní plánovače

Většina vývojářů aplikací nebo knihoven se nezajímá, na kterém procesoru běží úloha, jak synchronizuje svou práci s jinými úlohami nebo jak je naplánovaná na .System.Threading.ThreadPool Vyžadují pouze to, aby byly tyto úkony v hostitelském počítači prováděny tak efektivně, jak je to možné. Pokud potřebujete jemněji odstupňovanou kontrolu nad podrobnostmi plánování, TPL vám umožní nakonfigurovat některá nastavení výchozího plánovače úloh a dokonce vám umožní zadat vlastní plánovač. Další informace najdete na webu TaskScheduler.

TPL má několik nových veřejných typů, které jsou užitečné v paralelních a sekvenčních scénářích. Patří mezi ně několik tříd kolekcí bezpečných, rychlých a škálovatelných vláken v System.Collections.Concurrent oboru názvů a několik nových typů synchronizace. Například System.Threading.Semaphore a System.Threading.ManualResetEventSlim, které jsou efektivnější než jejich předchůdci pro konkrétní druhy úloh. Jiné nové typy v rozhraní .NET Framework 4, například System.Threading.Barrier a System.Threading.SpinLock, poskytují funkce, které nebyly k dispozici v dřívějších verzích. Další informace naleznete v tématu Datové struktury pro paralelní programování.

Vlastní typy úkolů

Doporučujeme, abyste nedědili z System.Threading.Tasks.Task nebo System.Threading.Tasks.Task<TResult>. Místo toho doporučujeme použít AsyncState vlastnost k přidružení dalších dat nebo stavu k objektu TaskTask<TResult> . K rozšíření funkcí Task a Task<TResult> tříd můžete také použít rozšiřující metody. Další informace o rozšiřujících metodách naleznete v tématu Metody rozšíření a metody rozšíření.

Pokud je nutné dědit z Task nebo Task<TResult>, nemůžete použít System.Threading.Tasks.TaskFactoryRunSystem.Threading.Tasks.TaskFactory<TResult>, nebo , nebo System.Threading.Tasks.TaskCompletionSource<TResult> třídy k vytvoření instancí vašeho vlastního typu úlohy. Nemůžete je použít, protože tyto třídy vytvářejí pouze Task a Task<TResult> objekty. Kromě toho nemůžete použít mechanismy pokračování úlohy, které poskytuje Task, Task<TResult>TaskFactorya TaskFactory<TResult> vytvářet instance vlastního typu úlohy. Nemůžete je použít, protože tyto třídy také vytvářejí pouze Task a Task<TResult> objekty.

Titulek Popis
Řetězení úloh pomocí úloh pokračování Popisuje, jak fungují pokračování.
Připojené a odpojené podřízené úlohy Popisuje rozdíl mezi připojenými a odpojenými podřízenými úlohami.
Zrušení úlohy Popisuje podporu zrušení, která je integrovaná do objektu Task .
Zpracování výjimek Popisuje způsob zpracování výjimek v souběžných vláknech.
Postupy: Použití algoritmu Parallel.Invoke k provádění paralelních operací Popisuje, jak používat Invoke.
Postupy: Vrácení hodnoty z úlohy Popisuje, jak z úloh vracet hodnoty.
Postupy: Zrušení úlohy a podřízených elementů Popisuje, jak zrušit úlohy.
Postupy: Vytváření předvypočítaných úloh Popisuje, jak použít metodu Task.FromResult k načtení výsledků asynchronních operací stahování uložených v mezipaměti.
Postupy: Procházení binárního stromu s paralelními úlohami Popisuje, jak použít úlohy k procházení binárního stromu.
Postupy: Rozbalení vnořené úlohy Ukazuje, jak použít metodu Unwrap rozšíření.
Datový paralelismus Popisuje použití For a ForEach vytváření paralelních smyček nad daty.
Paralelní programování Uzel nejvyšší úrovně pro paralelní programování rozhraní .NET Framework

Viz také