Aktivitetsbaserad asynkron programmering

Det parallella aktivitetsbiblioteket (TPL) baseras på begreppet aktivitet, som representerar en asynkron åtgärd. På sätt och vis liknar en uppgift en tråd eller ThreadPool ett arbetsobjekt men på en högre abstraktionsnivå. Termen aktivitetsparallellitet refererar till en eller flera oberoende aktiviteter som körs samtidigt. Uppgifter ger två huvudsakliga fördelar:

  • Effektivare och mer skalbar användning av systemresurser.

    I bakgrunden placeras uppgifter i ThreadPoolkö till , som har förbättrats med algoritmer som avgör och anpassar sig till antalet trådar. Dessa algoritmer ger belastningsutjämning för att maximera dataflödet. Den här processen gör uppgifterna relativt lätta och du kan skapa många av dem för att möjliggöra detaljerad parallellitet.

  • Mer programmatisk kontroll än vad som är möjligt med en tråd eller ett arbetsobjekt.

    Uppgifter och ramverket som bygger på dem ger en omfattande uppsättning API:er som stöder väntetid, annullering, fortsättningar, robust undantagshantering, detaljerad status, anpassad schemaläggning med mera.

Av båda anledningarna är TPL det föredragna API:et för att skriva multitrådad, asynkron och parallell kod i .NET.

Skapa och köra uppgifter implicit

Metoden Parallel.Invoke ger ett bekvämt sätt att köra valfritt antal godtyckliga instruktioner samtidigt. Skicka bara in ett Action ombud för varje arbetsobjekt. Det enklaste sättet att skapa dessa ombud är att använda lambda-uttryck. Lambda-uttrycket kan antingen anropa en namngiven metod eller ange koden infogad. I följande exempel visas ett grundläggande Invoke anrop som skapar och startar två uppgifter som körs samtidigt. Den första aktiviteten representeras av ett lambda-uttryck som anropar en metod med namnet DoSomeWork, och den andra aktiviteten representeras av ett lambda-uttryck som anropar en metod med namnet DoSomeOtherWork.

Kommentar

Den här dokumentationen använder lambda-uttryck för att definiera ombud i TPL. Om du inte är bekant med lambda-uttryck i C# eller Visual Basic kan du läsa Lambda-uttryck i PLINQ och TPL.

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

Kommentar

Antalet Task instanser som skapas i bakgrunden av Invoke är inte nödvändigtvis lika med antalet ombud som tillhandahålls. TPL kan använda olika optimeringar, särskilt med ett stort antal ombud.

Mer information finns i Så här använder du Parallel.Invoke för att köra parallella åtgärder.

Om du vill ha större kontroll över aktivitetskörningen eller för att returnera ett värde från aktiviteten måste du arbeta med Task objekt mer explicit.

Skapa och köra uppgifter explicit

En uppgift som inte returnerar ett värde representeras av System.Threading.Tasks.Task klassen. En uppgift som returnerar ett värde representeras av System.Threading.Tasks.Task<TResult> klassen, som ärver från Task. Aktivitetsobjektet hanterar infrastrukturinformationen och tillhandahåller metoder och egenskaper som är tillgängliga från den anropande tråden under aktivitetens livslängd. Du kan till exempel komma åt Status egenskapen för en aktivitet när som helst för att avgöra om den har börjat köras, körts till slutförd, avbrutits eller har genererat ett undantag. Statusen representeras av en TaskStatus uppräkning.

När du skapar en uppgift ger du den ett användardelegat som kapslar in koden som aktiviteten ska köra. Ombudet kan uttryckas som ett namngivet ombud, en anonym metod eller ett lambda-uttryck. Lambda-uttryck kan innehålla ett anrop till en namngiven metod, enligt följande exempel. Exemplet innehåller ett anrop till Task.Wait metoden för att säkerställa att aktiviteten slutför körningen innan konsolläget avslutas.

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

Du kan också använda Task.Run metoderna för att skapa och starta en uppgift i en åtgärd. För att hantera aktiviteten Run använder metoderna standardschemaläggaren, oavsett vilken schemaläggare som är associerad med den aktuella tråden. Metoderna Run är det bästa sättet att skapa och starta aktiviteter när mer kontroll över skapande och schemaläggning av uppgiften inte behövs.

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

Du kan också använda TaskFactory.StartNew metoden för att skapa och starta en uppgift i en åtgärd. Som du ser i följande exempel kan du använda den här metoden när:

  • Skapande och schemaläggning behöver inte separeras och du behöver ytterligare alternativ för att skapa aktiviteter eller använda en specifik schemaläggare.

  • Du måste skicka ytterligare tillstånd till den uppgift som du kan hämta via dess Task.AsyncState egenskap.

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 och var och Task<TResult> en exponerar en statisk Factory egenskap som returnerar en standardinstans av TaskFactory, så att du kan anropa metoden som Task.Factory.StartNew(). I följande exempel, eftersom aktiviteterna är av typen System.Threading.Tasks.Task<TResult>, har de var och en offentlig Task<TResult>.Result egenskap som innehåller resultatet av beräkningen. Aktiviteterna körs asynkront och kan slutföras i valfri ordning. Om egenskapen Result nås innan beräkningen är klar blockerar egenskapen den anropande tråden tills värdet är tillgängligt.

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

Mer information finns i Så här returnerar du ett värde från en aktivitet.

När du använder ett lambda-uttryck för att skapa ett ombud har du åtkomst till alla variabler som visas vid den tidpunkten i källkoden. Men i vissa fall, framför allt inom loopar, fångar en lambda inte variabeln som förväntat. Den samlar bara in referensen för variabeln, inte värdet, eftersom den muterar efter varje iteration. I följande exempel visas problemet. Den skickar en loopräknare till ett lambda-uttryck som instansierar ett CustomData objekt och använder loopräknaren som objektets identifierare. Som utdata från exemplet visar har varje CustomData objekt en identisk identifierare.

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

Du kan komma åt värdet för varje iteration genom att tillhandahålla ett tillståndsobjekt till en aktivitet via dess konstruktor. I följande exempel ändras det tidigare exemplet med hjälp av loopräknaren när du skapar CustomData objektet, som i sin tur skickas till lambda-uttrycket. Som utdata från exemplet visar har varje CustomData objekt nu en unik identifierare baserat på värdet för loopräknaren när objektet instansierades.

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

Det här tillståndet skickas som ett argument till uppgiftsdelegaten och kan nås från aktivitetsobjektet med hjälp Task.AsyncState av egenskapen . Följande exempel är en variant av föregående exempel. Den använder AsyncState egenskapen för att visa information om de objekt som CustomData skickas till lambda-uttrycket.

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

Aktivitets-ID

Varje uppgift får ett heltals-ID som unikt identifierar den i en programdomän och som kan nås med hjälp Task.Id av egenskapen . ID:t är användbart för att visa uppgiftsinformation i visual Studio-felsökningsprogrammets parallella staplar och uppgifter . ID:t skapas lazily, vilket innebär att det inte skapas förrän det har begärts. Därför kan en aktivitet ha ett annat ID varje gång programmet körs. Mer information om hur du visar aktivitets-ID:t i felsökningsprogrammet finns i Använda aktivitetsfönstret och Använda fönstret Parallella staplar.

Alternativ för att skapa aktiviteter

De flesta API:er som skapar uppgifter ger överlagringar som accepterar en TaskCreationOptions parameter. Genom att ange ett eller flera av dessa alternativ anger du för schemaläggaren hur aktiviteten ska schemaläggas i trådpoolen. Alternativ kan kombineras med hjälp av en bitvis OR-åtgärd .

I följande exempel visas en aktivitet med LongRunning alternativen och PreferFairness :

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

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

Uppgifter, trådar och kultur

Varje tråd har en associerad kultur och UI-kultur, som definieras av Thread.CurrentCulture egenskaperna och Thread.CurrentUICulture . En tråds kultur används i åtgärder som formatering, parsning, sortering och strängjämförelseåtgärder. En tråds användargränssnittskultur används i resurssökning.

Systemkulturen definierar standardkulturen och användargränssnittskulturen i en tråd. Du kan dock ange en standardkultur för alla trådar i en programdomän med hjälp CultureInfo.DefaultThreadCurrentCulture av egenskaperna och CultureInfo.DefaultThreadCurrentUICulture . Om du uttryckligen anger en tråds kultur och startar en ny tråd ärver inte den nya tråden kulturen i den anropande tråden. i stället är dess kultur standardsystemkulturen. I aktivitetsbaserad programmering använder uppgifter dock den anropande trådens kultur, även om aktiviteten körs asynkront på en annan tråd.

I följande exempel visas en enkel bild. Det ändrar appens nuvarande kultur till franska (Frankrike). Om franska (Frankrike) redan är den nuvarande kulturen, ändras den till engelska (USA). Sedan anropas ett ombud med namnet formatDelegate som returnerar vissa tal som är formaterade som valutavärden i den nya kulturen. Oavsett om ombudet anropas av en uppgift synkront eller asynkront använder aktiviteten kulturen i den anropande tråden.

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 €

Kommentar

I versioner av .NET Framework tidigare än .NET Framework 4.6 bestäms en aktivitets kultur av kulturen i tråden som den körs på, inte kulturen i den anropande tråden. För asynkrona uppgifter kan kulturen som används av aktiviteten skilja sig från den anropande trådens kultur.

Mer information om asynkrona uppgifter och kultur finns i avsnittet "Kultur och asynkrona aktivitetsbaserade åtgärder" i CultureInfo artikeln.

Skapa aktivitetsfortsättningar

Med Task.ContinueWith metoderna och Task<TResult>.ContinueWith kan du ange en uppgift som ska starta när den föregående aktiviteten är klar. Ombudet för fortsättningsaktiviteten skickas en referens till den tidigare aktiviteten så att den kan undersöka statusen för den föregående aktiviteten. Och genom att hämta värdet för Task<TResult>.Result egenskapen kan du använda utdata från antecedent som indata för fortsättningen.

I följande exempel startas getData uppgiften av ett anrop till TaskFactory.StartNew<TResult>(Func<TResult>) metoden. Uppgiften processData startas automatiskt när getData den är klar och displayData startas när processData den är klar. getData skapar en heltalsmatris som är tillgänglig för processData aktiviteten via getData aktivitetens Task<TResult>.Result egenskap. Aktiviteten processData bearbetar den matrisen och returnerar ett resultat vars typ härleds från returtypen för lambda-uttrycket som skickas Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) till metoden. Uppgiften displayData körs automatiskt när processData den är klar och objektet Tuple<T1,T2,T3> som returneras av processData lambda-uttrycket är tillgängligt för displayData aktiviteten via processData aktivitetens Task<TResult>.Result egenskap. Uppgiften displayData tar resultatet av processData aktiviteten. Det ger ett resultat vars typ härleds på ett liknande sätt och som görs tillgängligt för programmet i Result egenskapen.

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

Eftersom Task.ContinueWith är en instansmetod kan du kedja ihop metodanrop i stället för att instansiera ett Task<TResult> objekt för varje föregående uppgift. Följande exempel är funktionellt identiskt med det föregående, förutom att det kedjar samman anrop till Task.ContinueWith metoden. Objektet Task<TResult> som returneras av metodanropskedjan är den sista fortsättningsaktiviteten.

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

Med ContinueWhenAll metoderna och ContinueWhenAny kan du fortsätta från flera aktiviteter.

Mer information finns i Länka uppgifter med hjälp av fortsättningsuppgifter.

Skapa frånkopplade underordnade uppgifter

När användarkod som körs i en aktivitet skapar en ny aktivitet och inte anger AttachedToParent alternativet synkroniseras inte den nya aktiviteten med den överordnade aktiviteten på något särskilt sätt. Den här typen av icke-synkroniserad uppgift kallas för en fristående kapslad uppgift eller en frånkopplad underordnad aktivitet. I följande exempel visas en aktivitet som skapar en frånkopplad underordnad aktivitet:

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.

Kommentar

Den överordnade aktiviteten väntar inte på att den frånkopplade underordnade aktiviteten ska slutföras.

Skapa underordnade uppgifter

När användarkod som körs i en aktivitet skapar en uppgift med AttachedToParent alternativet kallas den nya aktiviteten för en kopplad underordnad uppgift för den överordnade aktiviteten. Du kan använda AttachedToParent alternativet för att uttrycka strukturerad aktivitetsparallellitet eftersom den överordnade aktiviteten implicit väntar på att alla anslutna underordnade aktiviteter ska slutföras. I följande exempel visas en överordnad uppgift som skapar 10 kopplade underordnade aktiviteter. I exemplet anropas Task.Wait metoden för att vänta tills den överordnade aktiviteten har slutförts. Den behöver inte uttryckligen vänta tills de anslutna underordnade uppgifterna har slutförts.

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

En överordnad uppgift kan använda alternativet TaskCreationOptions.DenyChildAttach för att förhindra att andra aktiviteter kopplas till den överordnade aktiviteten. Mer information finns i Bifogade och kopplade underordnade uppgifter.

Väntar på att aktiviteterna ska slutföras

Typerna System.Threading.Tasks.Task och System.Threading.Tasks.Task<TResult> innehåller flera överlagringar av de Task.Wait metoder som gör att du kan vänta tills en uppgift har slutförts. Dessutom kan överlagringar av statiska Task.WaitAll och Task.WaitAny metoder göra att du kan vänta tills en matris med uppgifter har slutförts.

Vanligtvis väntar du på en uppgift av någon av följande orsaker:

  • Huvudtråden beror på det slutliga resultatet som beräknas av en uppgift.

  • Du måste hantera undantag som kan genereras från uppgiften.

  • Programmet kan avslutas innan alla aktiviteter har slutfört körningen. Konsolprogram avslutas till exempel när all synkron kod i Main (programmets startpunkt) har körts.

I följande exempel visas det grundläggande mönstret som inte omfattar undantagshantering:

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

Ett exempel som visar undantagshantering finns i Undantagshantering.

Med vissa överlagringar kan du ange en tidsgräns, och andra tar ytterligare CancellationToken en som indataparameter så att själva väntetiden kan avbrytas antingen programmatiskt eller som svar på användarens indata.

När du väntar på en aktivitet väntar du implicit på alla underordnade aktiviteter som har skapats med hjälp TaskCreationOptions.AttachedToParent av alternativet . Task.Wait returnerar omedelbart om aktiviteten redan har slutförts. En Task.Wait metod utlöser eventuella undantag som genereras av en aktivitet, även om Task.Wait metoden anropades efter att aktiviteten har slutförts.

Skapa uppgifter

Klasserna Task och Task<TResult> innehåller flera metoder som hjälper dig att skapa flera uppgifter. Dessa metoder implementerar vanliga mönster och använder de asynkrona språkfunktioner som tillhandahålls av C#, Visual Basic och F#. I det här avsnittet beskrivs WhenAllmetoderna , WhenAny, Delayoch FromResult .

Task.WhenAll

Metoden Task.WhenAll väntar asynkront på att flera Task objekt eller Task<TResult> objekt ska slutföras. Den innehåller överlagrade versioner som gör att du kan vänta på icke-enhetliga uppsättningar med uppgifter. Du kan till exempel vänta tills flera Task objekt och Task<TResult> objekt har slutförts från ett metodanrop.

Task.WhenAny

Metoden Task.WhenAny väntar asynkront på att ett av flera Task objekt eller Task<TResult> objekt ska slutföras. Precis som i metoden tillhandahåller den Task.WhenAll här metoden överlagrade versioner som gör att du kan vänta på icke-enhetliga uppsättningar med uppgifter. Metoden WhenAny är särskilt användbar i följande scenarier:

  • Redundanta åtgärder: Överväg en algoritm eller åtgärd som kan utföras på många sätt. Du kan använda WhenAny metoden för att välja den åtgärd som slutförs först och sedan avbryta de återstående åtgärderna.

  • Interleaved-åtgärder: Du kan starta flera åtgärder som måste slutföras och använda WhenAny metoden för att bearbeta resultat när varje åtgärd slutförs. När en åtgärd har slutförts kan du starta en eller flera uppgifter.

  • Begränsade åtgärder: Du kan använda WhenAny metoden för att utöka föregående scenario genom att begränsa antalet samtidiga åtgärder.

  • Åtgärder som har upphört att gälla: Du kan använda WhenAny metoden för att välja mellan en eller flera aktiviteter och en aktivitet som avslutas efter en viss tid, till exempel en aktivitet som returneras av Delay metoden. Metoden Delay beskrivs i följande avsnitt.

Task.Delay

Metoden Task.Delay genererar ett Task objekt som avslutas efter den angivna tiden. Du kan använda den här metoden för att skapa loopar som söker efter data, för att ange tidsgränser, för att fördröja hanteringen av användarindata och så vidare.

Uppgift(T). FromResult

Med hjälp Task.FromResult av metoden kan du skapa ett Task<TResult> objekt som innehåller ett förberäknat resultat. Den här metoden är användbar när du utför en asynkron åtgärd som returnerar ett Task<TResult> objekt och resultatet av objektet Task<TResult> redan har beräknats. Ett exempel som använder FromResult för att hämta resultatet av asynkrona nedladdningsåtgärder som lagras i en cache finns i How to: Create Pre-Computed Tasks (Så här skapar du förberäknade uppgifter).

Hantera undantag i aktiviteter

När en uppgift utlöser ett eller flera undantag omsluts undantagen i ett AggregateException undantag. Undantaget sprids tillbaka till tråden som ansluter till uppgiften. Vanligtvis är det tråden som väntar på att uppgiften ska slutföras eller tråden som Result kommer åt egenskapen. Det här beteendet tillämpar .NET Framework-principen att alla ohanterade undantag som standard ska avsluta processen. Den anropande koden kan hantera undantagen med något av följande i ett try/catch block:

Kopplingstråden kan också hantera undantag genom att Exception komma åt egenskapen innan uppgiften samlas in. Genom att komma åt den här egenskapen förhindrar du att det ohanterade undantaget utlöser det undantagsspridningsbeteende som avslutar processen när objektet har slutförts.

Mer information om undantag och uppgifter finns i Undantagshantering.

Avbryta uppgifter

Klassen Task stöder kooperativ annullering och är helt integrerad med klasserna System.Threading.CancellationTokenSource och System.Threading.CancellationToken som introducerades i .NET Framework 4. Många av konstruktorerna i System.Threading.Tasks.Task klassen tar ett CancellationToken objekt som en indataparameter. Många av överlagringarna StartNew och Run innehåller också en CancellationToken parameter.

Du kan skapa token och utfärda annulleringsbegäran vid ett senare tillfälle med hjälp CancellationTokenSource av klassen . Skicka token till som ett argument och referera även till Task samma token i ditt användardelegat, vilket gör arbetet med att svara på en begäran om annullering.

Mer information finns i Annullering av aktiviteter och Så här avbryter du en aktivitet och dess underordnade aktiviteter.

Klassen TaskFactory

Klassen TaskFactory innehåller statiska metoder som kapslar in vanliga mönster för att skapa och starta uppgifter och fortsättningsuppgifter.

Standardvärdet TaskFactory kan nås som en statisk egenskap för Task klassen eller Task<TResult> klassen. Du kan också instansiera en TaskFactory direkt och ange olika alternativ som innehåller ett CancellationToken, ett TaskCreationOptions alternativ, ett TaskContinuationOptions alternativ eller en TaskScheduler. De alternativ som anges när du skapar aktivitetsfabriken tillämpas på alla aktiviteter som skapas om inte Task skapas med hjälp TaskCreationOptions av uppräkningen, i vilket fall aktivitetens alternativ åsidosätter aktivitetsfabrikens.

Uppgifter utan ombud

I vissa fall kanske du vill använda en Task för att kapsla in en asynkron åtgärd som utförs av en extern komponent i stället för ditt användardelegat. Om åtgärden baseras på mönstret Asynkron programmeringsmodell för start/slut kan du använda FromAsync metoderna. Om så inte är fallet kan du använda TaskCompletionSource<TResult> objektet för att omsluta åtgärden i en uppgift och därmed få några av fördelarna med Task programmering. Till exempel stöd för undantagsspridning och fortsättningar. Mer information finns i TaskCompletionSource<TResult>.

Anpassade schemaläggare

De flesta program- eller biblioteksutvecklare bryr sig inte om vilken processor uppgiften körs på, hur den synkroniserar sitt arbete med andra uppgifter eller hur den schemaläggs på System.Threading.ThreadPool. De kräver bara att den körs så effektivt som möjligt på värddatorn. Om du behöver mer detaljerad kontroll över schemaläggningsinformationen kan du med TPL konfigurera vissa inställningar i standardschemaläggaren och även ange en anpassad schemaläggare. Mer information finns i TaskScheduler.

TPL har flera nya offentliga typer som är användbara i parallella och sekventiella scenarier. Dessa omfattar flera trådsäkra, snabba och skalbara samlingsklasser i System.Collections.Concurrent namnområdet och flera nya synkroniseringstyper. Till exempel System.Threading.Semaphore och System.Threading.ManualResetEventSlim, som är mer effektiva än sina föregångare för specifika typer av arbetsbelastningar. Andra nya typer i .NET Framework 4, till exempel System.Threading.Barrier och System.Threading.SpinLock, tillhandahåller funktioner som inte var tillgängliga i tidigare versioner. Mer information finns i Datastrukturer för parallell programmering.

Anpassade aktivitetstyper

Vi rekommenderar att du inte ärver från System.Threading.Tasks.Task eller System.Threading.Tasks.Task<TResult>. I stället rekommenderar vi att du använder AsyncState egenskapen för att associera ytterligare data eller tillstånd med ett eller Task<TResult> -Taskobjekt. Du kan också använda tilläggsmetoder för att utöka funktionerna i klasserna Task och Task<TResult> . Mer information om tilläggsmetoder finns i Tilläggsmetoder och tilläggsmetoder.

Om du måste ärva från Task eller Task<TResult>kan du inte använda Run eller klasserna System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>eller för System.Threading.Tasks.TaskCompletionSource<TResult> att skapa instanser av din anpassade aktivitetstyp. Du kan inte använda dem eftersom dessa klasser endast Task skapar och Task<TResult> objekt. Dessutom kan du inte använda de fortsättningsmekanismer för aktiviteter som tillhandahålls av Task, Task<TResult>, TaskFactoryoch TaskFactory<TResult> för att skapa instanser av din anpassade aktivitetstyp. Du kan inte använda dem eftersom dessa klasser också bara Task skapar och Task<TResult> objekt.

Title Description
Länka aktiviteter med hjälp av fortsättningsaktiviteter Beskriver hur fortsättningar fungerar.
Kopplade och frånkopplade underordnade uppgifter Beskriver skillnaden mellan anslutna och frånkopplade underordnade uppgifter.
Aktivitetsavbokning Beskriver stödet för annullering som är inbyggt i Task objektet.
Undantagshantering Beskriver hur undantag i samtidiga trådar hanteras.
Gör så här: Använd Parallel.Invoke för att köra parallella åtgärder Beskriver hur du använder Invoke.
Gör så här: Returnera ett värde från en aktivitet Beskriver hur du returnerar värden från aktiviteter.
Gör så här: Avbryt en aktivitet och dess underordnade Beskriver hur du avbryter aktiviteter.
Anvisningar: Skapa förberäknade uppgifter Beskriver hur du använder Task.FromResult metoden för att hämta resultatet av asynkrona nedladdningsåtgärder som lagras i en cache.
Anvisningar: Bläddra i ett binärt träd med parallella uppgifter Beskriver hur du använder uppgifter för att korsa ett binärt träd.
Gör så här: Packa upp en kapslad uppgift Visar hur du använder Unwrap tilläggsmetoden.
Dataparallellitet Beskriver hur du använder For och ForEach skapar parallella loopar över data.
Parallell programmering Nod på översta nivån för parallell programmering med .NET Framework.

Se även