Feladatalapú aszinkron programozás

A tevékenység párhuzamos kódtára (TPL) egy tevékenység koncepcióján alapul, amely egy aszinkron műveletet jelöl. Bizonyos módokon egy tevékenység hasonlít egy szálra vagy ThreadPool munkaelemre, de magasabb absztrakciós szinten. A tevékenység-párhuzamosság kifejezés egy vagy több, egyidejűleg futó független tevékenységre utal. A feladatok két elsődleges előnyt biztosítanak:

  • A rendszererőforrások hatékonyabb és skálázhatóbb használata.

    A színfalak mögött a feladatok a sorba ThreadPoolkerülnek, amelyet olyan algoritmusok egészítettek ki, amelyek meghatározzák és igazodnak a szálak számához. Ezek az algoritmusok terheléselosztást biztosítanak az átviteli sebesség maximalizálása érdekében. Ez a folyamat viszonylag egyszerűvé teszi a feladatokat, és számosat létrehozhat a részletes párhuzamosság érdekében.

  • Programozottabb vezérlés, mint egy szál vagy munkaelem esetén lehetséges.

    A feladatok és az ezek köré épített keretrendszer számos olyan API-t biztosítanak, amelyek támogatják a várakozást, a lemondást, a folytatásokat, a robusztus kivételkezelést, a részletes állapotot, az egyéni ütemezést és egyebeket.

Mindkét okból a TPL az elsődleges API a többszálas, aszinkron és párhuzamos kód írásához a .NET-ben.

Tevékenységek implicit létrehozása és futtatása

A Parallel.Invoke módszer kényelmes módot kínál tetszőleges számú utasítás egyidejű futtatására. Csak adjon át egy meghatalmazottat Action minden egyes munkaelemhez. A meghatalmazottak létrehozásának legegyszerűbb módja a lambdakifejezések használata. A lambda kifejezés meghívhat egy elnevezett metódust, vagy megadhatja a kódot beágyazottan. Az alábbi példa egy alapszintű Invoke hívást mutat be, amely két egyidejűleg futó feladatot hoz létre és indít el. Az első feladatot egy lambda kifejezés képviseli, amely egy nevesített DoSomeWorkmetódust hív meg, a második feladatot pedig egy lambda kifejezés képviseli, amely egy nevesített DoSomeOtherWorkmetódust hív meg.

Feljegyzés

Ez a dokumentáció lambdakifejezéseket használ a meghatalmazottak definiálásához a TPL-ben. Ha nem ismeri a lambda kifejezéseket a C# vagy a Visual Basic alkalmazásban, tekintse meg a Lambda-kifejezéseket a PLINQ-ban és a TPL-ben.

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

Feljegyzés

A színfalak Invoke mögött létrehozott példányok száma Task nem feltétlenül egyenlő a megadott meghatalmazottak számával. A TPL különböző optimalizálásokat alkalmazhat, különösen nagy számú meghatalmazott esetén.

További információ : How to: Use Parallel.Invoke to Execute Parallel Operations.

A tevékenység végrehajtásának nagyobb mértékű szabályozásához vagy a tevékenységből származó érték visszaadásához explicitebb módon kell dolgoznia az objektumokkal Task .

Tevékenységek explicit létrehozása és futtatása

Az értéket nem visszaadó feladatokat az System.Threading.Tasks.Task osztály jelöli. Az értéket visszaadó feladatokat az System.Threading.Tasks.Task<TResult> osztály jelöli, amely a következőtől Tasköröklődik: A tevékenységobjektum kezeli az infrastruktúra részleteit, és olyan metódusokat és tulajdonságokat biztosít, amelyek a hívó szálról érhetők el a tevékenység teljes élettartama során. Egy tevékenység tulajdonságához bármikor hozzáférhet Status , így megállapíthatja, hogy a tevékenység elindult-e, futott-e a befejezésig, megszakította-e, vagy kivételt okozott-e. Az állapotot enumerálás TaskStatus jelöli.

Amikor létrehoz egy feladatot, egy felhasználói meghatalmazottat ad neki, aki beágyazza a feladat által végrehajtandó kódot. A delegált kifejezhető elnevezett delegáltként, névtelen metódusként vagy lambdakifejezésként. A Lambda-kifejezések tartalmazhatnak egy elnevezett metódusra irányuló hívást, ahogy az az alábbi példában is látható. A példa egy metódusra irányuló Task.Wait hívást is tartalmaz, amely biztosítja, hogy a feladat a konzol módú alkalmazás befejeződése előtt befejeződjön.

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

A metódusokkal egy műveletben is Task.Run létrehozhat és elindíthat feladatokat. A tevékenység kezeléséhez a Run metódusok az alapértelmezett feladatütemezőt használják, függetlenül attól, hogy melyik tevékenységütemező van társítva az aktuális szálhoz. A Run metódusok a tevékenységek létrehozásának és elindításának előnyben részesített módja, ha nincs szükség a tevékenység létrehozására és ütemezésére.

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

A metódussal TaskFactory.StartNew egy műveletben is létrehozhat és elindíthat feladatokat. Az alábbi példában látható módon ezt a módszert akkor használhatja, ha:

  • A létrehozást és az ütemezést nem kell különválasztani, és további feladatlétrehozási lehetőségekre vagy adott ütemező használatára van szükség.

  • További állapotot kell átadnia a feladatnak, amelyet a tulajdonságán Task.AsyncState keresztül lekérhet.

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 és Task<TResult> mindegyik egy statikus Factory tulajdonságot tesz elérhetővé, amely egy alapértelmezett példányt TaskFactoryad vissza, így a metódust Task.Factory.StartNew()a következőképpen hívhatja meg. Emellett a következő példában, mivel a tevékenységek típusa típus System.Threading.Tasks.Task<TResult>, mindegyiknek van egy nyilvános Task<TResult>.Result tulajdonsága, amely tartalmazza a számítás eredményét. A feladatok aszinkron módon futnak, és bármilyen sorrendben befejeződhetnek. Ha a tulajdonság a Result számítás befejezése előtt érhető el, a tulajdonság blokkolja a hívó szálat, amíg az érték el nem érhető.

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

További információ : How to: Return a Value from a Task.

Ha lambda kifejezéssel hoz létre meghatalmazottat, hozzáférhet a forráskód ezen pontján látható összes változóhoz. Bizonyos esetekben azonban, különösen a hurkokban, a lambda nem a várt módon rögzíti a változót. Csak a változó hivatkozását rögzíti, nem az értéket, mivel az egyes iterációk után mutál. Az alábbi példa a problémát szemlélteti. Egy hurokszámlálót ad át egy lambda kifejezésnek, amely létrehoz egy CustomData objektumot, és a hurokszámlálót használja az objektum azonosítójaként. Ahogy a példa kimenete is mutatja, minden CustomData objektum azonos azonosítóval rendelkezik.

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

Az egyes iterációk értékeit úgy érheti el, hogy a konstruktoron keresztül egy állapotobjektumot ad egy feladatnak. Az alábbi példa módosítja az előző példát az objektum létrehozásakor CustomData használt hurokszámlálóval, amelyet a rendszer átad a lambda kifejezésnek. Ahogy a példa kimenete is mutatja, minden CustomData objektum egyedi azonosítóval rendelkezik az objektum példányosításakor a hurokszámláló értéke alapján.

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

Ezt az állapotot a rendszer argumentumként továbbítja a feladatmegbízottnak, és a tulajdonság használatával elérhető a Task.AsyncState tevékenységobjektumból. Az alábbi példa az előző példában szereplő változat. A tulajdonság használatával AsyncState jeleníti meg a CustomData lambda kifejezésnek átadott objektumok adatait.

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

Tevékenységazonosító

Minden tevékenység kap egy egész számazonosítót, amely egyedileg azonosítja azt egy alkalmazástartományban, és a tulajdonság használatával Task.Id érhető el. Az azonosító a Visual Studio párhuzamos halmok és feladatok ablakában található feladatinformációk megtekintéséhez hasznos. Az azonosító lazán jön létre, ami azt jelenti, hogy csak a kérés után jön létre. Ezért előfordulhat, hogy egy tevékenység minden futtatásakor más azonosítóval rendelkezik. A feladatazonosítók hibakeresőben való megtekintéséről további információt a Tevékenységek ablak és a Párhuzamos halmok ablak használata című témakörben talál.

Feladatlétrehozás beállításai

A feladatokat létrehozó API-k többsége túlterheléseket biztosít, amelyek elfogadják a paramétert TaskCreationOptions . Egy vagy több beállítás megadásával megadhatja a feladatütemezőnek, hogyan ütemezze a feladatot a szálkészleten. A beállítások bitenkénti VAGY művelettel kombinálhatók.

Az alábbi példa egy olyan feladatot mutat be, amely rendelkezik a LongRunning következő lehetőségekkel: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()

Feladatok, szálak és kultúra

Minden szál rendelkezik egy társított kultúrával és felhasználói felületi kultúrával, amelyet az és Thread.CurrentUICulture a Thread.CurrentCulture tulajdonságok határoznak meg. A szál kultúrája olyan műveletekben használatos, mint a formázás, az elemzés, a rendezés és a sztring-összehasonlító műveletek. A szál felhasználói felületi kultúrája az erőforrás-keresésben használatos.

A rendszerkultúra határozza meg egy szál alapértelmezett kulturális és felhasználói felületi kultúráját. Az alkalmazástartomány összes szálához azonban megadhat egy alapértelmezett kultúrát a tulajdonságok és CultureInfo.DefaultThreadCurrentUICulture a CultureInfo.DefaultThreadCurrentCulture tulajdonságok használatával. Ha explicit módon állítja be egy szál kultúráját, és elindít egy új szálat, az új szál nem örökli a hívó szál kultúráját; ehelyett a kultúra az alapértelmezett rendszerkultúra. A feladatalapú programozásban azonban a tevékenységek a hívószál kultúráját használják, még akkor is, ha a tevékenység aszinkron módon fut egy másik szálon.

Az alábbi példa egy egyszerű ábrát mutat be. Az alkalmazás jelenlegi kultúráját francia (Franciaország) nyelvre módosítja. Ha a francia (Franciaország) már a jelenlegi kultúra, akkor változik az angol (Egyesült Államok). Ezután meghív egy meghatalmazottat formatDelegate , aki az új kultúrában pénznemértékként formázott számokat ad vissza. Függetlenül attól, hogy a meghatalmazottat egy tevékenység szinkron vagy aszinkron módon hívja meg, a tevékenység a hívószál kultúráját használja.

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 €

Feljegyzés

A .NET-keretrendszer 4.6-.NET-keretrendszer osnál korábbi verzióiban a feladat kultúráját annak a szálnak a kultúrája határozza meg, amelyen fut, nem pedig a hívószál kultúrája. Az aszinkron feladatok esetében a tevékenység által használt kultúra eltérhet a hívószál kultúrájától.

Az aszinkron tevékenységekkel és kultúrával kapcsolatos további információkért lásd a cikk "Kultúra és aszinkron tevékenységalapú műveletek" című szakaszát CultureInfo .

Tevékenység folytatásainak létrehozása

A Task.ContinueWith metódusokkal Task<TResult>.ContinueWith megadhat egy tevékenységet, amely az előzménytevékenység befejeződésekor indul el. A folytatási tevékenység delegáltja az előzménytevékenységre mutató hivatkozást kap, hogy megvizsgálhassa az előzménytevékenység állapotát. A tulajdonság értékének Task<TResult>.Result beolvasásával pedig az előzmény kimenetét használhatja bemenetként a folytatáshoz.

Az alábbi példában a getData feladatot a metódus hívása TaskFactory.StartNew<TResult>(Func<TResult>) indítja el. A processData feladat automatikusan elindul a befejezéskor getData , és displayData a befejezéskor processData indul el. getDataegy egész számtömböt hoz létre, amely a processData tevékenység tulajdonságán Task<TResult>.Result keresztül érhető el.getData A processData feladat feldolgozza a tömböt, és visszaad egy eredményt, amelynek típusa a metódusnak átadott lambda kifejezés visszatérési típusából származik Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) . A displayData tevékenység automatikusan végrehajtja a befejezéstprocessData, és a Tuple<T1,T2,T3>processData lambda kifejezés által visszaadott objektum a displayData tevékenység tulajdonságán Task<TResult>.Result keresztül érhető el a processData tevékenység számára. A displayData tevékenység a tevékenység eredményét veszi át processData . Olyan eredményt hoz létre, amelynek típusa hasonló módon következtethető ki, és amely a tulajdonságban lévő program számára elérhetővé válik Result .

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

Mivel Task.ContinueWith példánymetódusról van szó, a metódushívások összefűzhetők ahelyett, hogy az Task<TResult> egyes előzményfeladatok objektumait példányosítanák. Az alábbi példa funkcionálisan megegyezik az előző példával, azzal a kivételével, hogy a metódushoz Task.ContinueWith intézett hívásokat összekapcsolja. A Task<TResult> metódushívások lánca által visszaadott objektum az utolsó folytatási feladat.

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 A ContinueWhenAll metódusokkal több tevékenységből is folytathatja a munkát.

További információ: Tevékenységek láncolása folytatási tevékenységek használatával.

Leválasztott gyermekfeladatok létrehozása

Amikor egy tevékenységben futó felhasználói kód létrehoz egy új feladatot, és nem adja meg a AttachedToParent beállítást, az új tevékenység semmilyen különleges módon nem szinkronizálva lesz a szülőtevékenységgel. Ezt a nem szinkronizált feladatot leválasztott beágyazott vagy leválasztott gyermekfeladatnak nevezzük. Az alábbi példa egy olyan feladatot mutat be, amely egy leválasztott gyermekfeladatot hoz létre:

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.

Feljegyzés

A szülőfeladat nem várja meg, amíg a leválasztott gyermekfeladat befejeződik.

Gyermekfeladatok létrehozása

Amikor egy tevékenységben futó felhasználói kód létrehoz egy feladatot a AttachedToParent beállítással, az új feladatot a szülőtevékenység csatolt gyermekfeladatának nevezzük. A lehetőséggel kifejezheti a AttachedToParent strukturált tevékenységek párhuzamosságát, mivel a szülőtevékenység implicit módon megvárja az összes csatolt gyermektevékenység befejezését. Az alábbi példa egy szülőfeladatot mutat be, amely 10 csatolt gyermekfeladatot hoz létre. A példa meghívja a metódust Task.Wait , hogy várja meg, amíg a szülőfeladat befejeződik. Nem kell explicit módon várnia, amíg a csatolt gyermekfeladatok befejeződnek.

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

A szülőtevékenységek ezzel a TaskCreationOptions.DenyChildAttach beállítással megakadályozhatják, hogy más tevékenységek kapcsolódjanak a szülőtevékenységhez. További információ: Csatolt és leválasztott gyermekfeladatok.

Várakozás a tevékenységek befejezésére

Az System.Threading.Tasks.Task és System.Threading.Tasks.Task<TResult> a típusok számos olyan metódust Task.Wait biztosítanak, amelyek lehetővé teszik a tevékenységek befejezésére való várakozást. Emellett a statikus Task.WaitAll és Task.WaitAny a metódusok túlterhelése lehetővé teszi, hogy megvárja, amíg a tevékenységek egy vagy több tömbje befejeződik.

Általában az alábbi okok valamelyike miatt várna egy feladatra:

  • A fő szál a tevékenység által kiszámított végső eredménytől függ.

  • Kezelnie kell a tevékenységből esetleg kidobott kivételeket.

  • Az alkalmazás az összes tevékenység végrehajtása előtt leállhat. A konzolalkalmazások például az összes szinkron kód Main (az alkalmazás belépési pontja) végrehajtása után leállnak.

Az alábbi példa azt az alapszintű mintát mutatja be, amely nem jár kivételkezeléssel:

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

A kivételkezelést bemutató példát a Kivételkezelés című témakörben talál.

Egyes túlterhelések lehetővé teszik az időtúllépés megadását, míg mások további CancellationToken beviteli paramétert vesznek igénybe, hogy maga a várakozás programozott módon vagy a felhasználói bemenetre reagálva megszakítható legyen.

Amikor egy tevékenységre vár, implicit módon várja meg a feladat összes gyermekét, akik a TaskCreationOptions.AttachedToParent beállítással lettek létrehozva. Task.Wait azonnal visszaadja, ha a tevékenység már befejeződött. A Task.Wait metódusok akkor is kivételt okoznak, ha a Task.Wait metódus a tevékenység befejezése után lett meghívva.

Feladatok írása

Az Task osztályok számos Task<TResult> módszert kínálnak több feladat összeállításához. Ezek a módszerek közös mintákat alkalmaznak, és jobban kihasználják a C#, a Visual Basic és az F# által biztosított aszinkron nyelvi funkciókat. Ez a szakasz a WhenAll, WhenAny, Delayés FromResult metódusokat ismerteti.

Task.WhenAll

A Task.WhenAll metódus aszinkron módon több vagy Task<TResult> objektum Task befejezésére vár. Túlterhelt verziókat biztosít, amelyek lehetővé teszik a nem egységes feladatkészletek várakozását. Megvárhatja például, amíg több Task objektum és Task<TResult> objektum befejeződik egy metódushívásból.

Task.WhenAny

A Task.WhenAny metódus aszinkron módon megvárja, amíg több Task vagy Task<TResult> objektum valamelyike befejeződik. A módszerhez hasonlóan ez a Task.WhenAll módszer túlterhelt verziókat is biztosít, amelyek lehetővé teszik a nem egységes feladatkészletek várakozását. A WhenAny módszer különösen a következő esetekben hasznos:

  • Redundáns műveletek: Fontolja meg az algoritmust vagy a műveletet, amely sokféleképpen végrehajtható. A metódus használatával WhenAny kiválaszthatja az elsőként befejezett műveletet, majd megszakíthatja a fennmaradó műveleteket.

  • Interleaved műveletek: Több olyan műveletet is elindíthat, amelyeknek be kell fejeződniük, és a metódus használatával feldolgozhatók az WhenAny eredmények az egyes műveletek befejezésekor. Miután egy művelet befejeződött, elindíthat egy vagy több feladatot.

  • Szabályozott műveletek: A WhenAny metódussal kibővítheti az előző forgatókönyvet az egyidejű műveletek számának korlátozásával.

  • Lejárt műveletek: A metódussal WhenAny választhat egy vagy több tevékenység és egy adott időpont után befejezett tevékenység között, például egy, a Delay metódus által visszaadott tevékenység közül. A Delay metódust a következő szakaszban ismertetjük.

Task.Delay

A Task.Delay metódus létrehoz egy Task objektumot, amely a megadott idő után fejeződik be. Ezzel a módszerrel olyan hurkokat hozhat létre, amelyek lekérdezik az adatokat, időtúllépéseket határoznak meg, késleltetik a felhasználói bemenetek kezelését stb.

Feladat(T). FromResult

A metódus használatával Task.FromResult létrehozhat egy Task<TResult> objektumot, amely előre kiszámított eredményt tartalmaz. Ez a módszer akkor hasznos, ha egy objektumot visszaadó Task<TResult> aszinkron műveletet hajt végre, és az objektum eredménye Task<TResult> már ki van számítva. A gyorsítótárban tárolt aszinkron letöltési műveletek eredményeinek lekérésére szolgáló FromResult példa: Útmutató: Előre kiszámított feladatok létrehozása.

Kivételek kezelése a tevékenységekben

Ha egy tevékenység egy vagy több kivételt ad ki, a kivételek kivételbe AggregateException vannak csomagolva. Ezt a kivételt a rendszer a tevékenységhez csatlakozó szálra propagálja. Általában az a szál vár a feladat befejezésére, vagy a szál, amely hozzáfér a Result tulajdonsághoz. Ez a viselkedés kikényszeríti a .NET-keretrendszer szabályzatot, hogy alapértelmezés szerint minden kezeletlen kivételnek le kell mondania a folyamatot. A hívókód az alábbi blokkok try/catch bármelyikével kezelheti a kivételeket:

Az összekapcsolási szál a kivételeket úgy is kezelheti, hogy a tevékenység szemétgyűjtése előtt hozzáfér Exception a tulajdonsághoz. A tulajdonság elérésével megakadályozhatja, hogy a kezeletlen kivétel kiváltsa azt a kivételpropagálási viselkedést, amely az objektum véglegesítésekor leállítja a folyamatot.

A kivételekről és feladatokról további információt a Kivételkezelés című témakörben talál.

Tevékenységek megszakítása

Az Task osztály támogatja a kooperatív lemondást, és teljes mértékben integrálva van a System.Threading.CancellationTokenSourceSystem.Threading.CancellationToken .NET-keretrendszer 4. Az osztály számos konstruktora System.Threading.Tasks.Task egy objektumot fogad CancellationToken bemeneti paraméterként. StartNew Sok és Run túlterhelés is tartalmaz egy paramétertCancellationToken.

Az osztály használatával létrehozhatja a jogkivonatot, és később kibocsáthatja a lemondási CancellationTokenSource kérelmet. Adja át a jogkivonatot Task argumentumként, és hivatkozzon ugyanarra a jogkivonatra a felhasználói meghatalmazottban is, amely elvégzi a lemondási kérelmek megválaszolását.

További információ: Feladat lemondása és útmutató: Tevékenység lemondása és gyermekei.

A TaskFactory osztály

Az TaskFactory osztály statikus metódusokat biztosít, amelyek közös mintákat foglalnak össze a tevékenységek létrehozásához és elindításához, valamint a tevékenységek folytatásához.

Az alapértelmezett TaskFactory érték statikus tulajdonságként érhető el az osztályon vagy Task<TResult> osztályonTask. Közvetlenül is létrehozhat egy példánytTaskFactory, és megadhatja a különböző beállításokat, például egy CancellationTokenlehetőséget, egy TaskContinuationOptionsTaskCreationOptions lehetőséget vagy egy TaskSchedulerlehetőséget. A tevékenység-előállító létrehozásakor megadott beállításokat a rendszer az összes létrehozott tevékenységre alkalmazza, kivéve, ha az Task enumerálással TaskCreationOptions jön létre, ebben az esetben a tevékenység beállításai felülírják a feladat-előállító beállításait.

Meghatalmazottak nélküli feladatok

Bizonyos esetekben érdemes lehet egy Task olyan aszinkron műveletet beágyazni, amelyet a felhasználói meghatalmazott helyett egy külső összetevő hajt végre. Ha a művelet az aszinkron programozási modell kezdő/záró mintáján alapul, használhatja a FromAsync metódusokat. Ha ez nem így van, az TaskCompletionSource<TResult> objektummal körbefuttathatja a műveletet egy feladatban, és ezáltal kihasználhatja a programozhatóság néhány előnyét Task . Például a kivételek propagálásának és folytatásának támogatása. További információ: TaskCompletionSource<TResult>.

Egyéni ütemezők

A legtöbb alkalmazás- vagy tárfejlesztő nem törődik azzal, hogy a feladat melyik processzoron fut, hogyan szinkronizálja a munkáját más feladatokkal, vagy hogyan ütemezi a feladatokat a System.Threading.ThreadPool. Csak a lehető leghatékonyabb végrehajtást követelik meg a gazdaszámítógépen. Ha részletesebb vezérlésre van szüksége az ütemezés részletei felett, a TPL lehetővé teszi bizonyos beállítások konfigurálását az alapértelmezett feladatütemezőn, és akár egyéni ütemezőt is megadhat. További információ: TaskScheduler.

A TPL számos új nyilvános típust használ, amelyek párhuzamos és szekvenciális forgatókönyvekben hasznosak. Ezek közé tartozik a névtér több szálbiztos, gyors és méretezhető gyűjteményosztálya System.Collections.Concurrent , valamint számos új szinkronizálási típus. Például System.Threading.Semaphore , és System.Threading.ManualResetEventSlimamelyek hatékonyabbak, mint elődjeik bizonyos típusú számítási feladatokhoz. A .NET-keretrendszer 4 egyéb új típusai például olyan funkciókat biztosítanak, System.Threading.BarrierSystem.Threading.SpinLockamelyek a korábbi kiadásokban nem érhetők el. További információ: Adatstruktúrák párhuzamos programozáshoz.

Egyéni tevékenységtípusok

Azt javasoljuk, hogy ne örököljön a forrástól System.Threading.Tasks.Task vagy System.Threading.Tasks.Task<TResult>a . Ehelyett azt javasoljuk, hogy a AsyncState tulajdonság használatával társítsa a további adatokat vagy állapotokat egy vagy Task<TResult> objektumhozTask. A bővítménymetelyekkel bővítheti az osztályok és Task<TResult> az osztályok funkcióit Task is. A bővítménymetelyekről további információt a Bővítménymetelyek és a Bővítménymetelyek című témakörben talál.

Ha örökölnie Task kell az Task<TResult>egyéni feladattípus példányait, akkor nem használhatja Run vagy használhatja a System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>vagy System.Threading.Tasks.TaskCompletionSource<TResult> osztályokat. Nem használhatja őket, mert ezek az osztályok csak Task és Task<TResult> objektumokat hoznak létre. Emellett nem használhatja a tevékenység folytatására szolgáló mechanizmusokat, amelyeket a , Task<TResult>, TaskFactoryés TaskFactory<TResult> az egyéni tevékenységtípus példányainak létrehozásához biztosítTask. Nem használhatja őket, mert ezek az osztályok csak Task és Task<TResult> objektumokat is létrehoznak.

Cím Leírás
Tevékenységek láncolása folytatási tevékenységek használatával A folytatások működését ismerteti.
Csatolt és leválasztott gyermekfeladatok A csatolt és a leválasztott gyermekfeladatok közötti különbséget ismerteti.
Tevékenység lemondása Az objektumba beépített lemondási Task támogatást ismerteti.
Kivételkezelés Az egyidejű szálak kivételeinek kezelését ismerteti.
Útmutató: A Parallel.Invoke használata párhuzamos műveletek végrehajtásához A használat Invokemódját ismerteti.
Útmutató: Érték visszaadása tevékenységből Azt ismerteti, hogyan ad vissza értékeket a tevékenységekből.
Útmutató: Feladat és gyermekei lemondása A tevékenységek megszakításának módját ismerteti.
Útmutató: Előre kiszámított feladatok létrehozása Ismerteti, hogyan használható a metódus a Task.FromResult gyorsítótárban tárolt aszinkron letöltési műveletek eredményeinek lekérésére.
Útmutató: Bináris fa átjárása párhuzamos feladatokkal Ez a cikk azt ismerteti, hogyan használhatja a feladatokat egy bináris fa bejárására.
Útmutató: Beágyazott feladat kibontása Bemutatja a bővítménymetódus Unwrap használatát.
Adat-párhuzamosság Bemutatja, hogyan használhatók For és ForEach hozhatók létre párhuzamos hurkok az adatokon.
Párhuzamos programozás A párhuzamos programozás .NET-keretrendszer legfelső szintű csomópontja.

Lásd még