Devamlılık görevlerini kullanarak görevleri zincirleme

Zaman uyumsuz programlamada, bir zaman uyumsuz işlemin tamamlandığında ikinci bir işlemi çağırması yaygındır. Devamlılıklar, alt işlemlerin ilk işlemin sonuçlarını tüketmesine olanak tanır. Geleneksel olarak, devamlılıklar geri çağırma yöntemleri kullanılarak yapılır. Görev Paralel Kitaplığı'nda (TPL), aynı işlevsellik devamlılık görevleri tarafından sağlanır. Devamlılık görevi (aynı zamanda devamlılık olarak da bilinir), öncül olarak bilinen başka bir görev tarafından, öncül tamamlandığında çağrılan zaman uyumsuz bir görevdir.

Devamlılıkların kullanımı nispeten kolaydır, ancak yine de güçlü ve esnektir. Örneğin, şunları yapabilirsiniz:

  • Öncülden devama veri geçirme.
  • Devamın çağrılacağı veya çağrılmayacak olduğu kesin koşulları belirtin.
  • Devamı başlamadan önce veya çalışırken işbirliğiyle iptal edin.
  • Devamın nasıl zamanlanması gerektiği hakkında ipuçları sağlayın.
  • Aynı öncülden birden çok devamlılık çağırın.
  • Birden çok öncülden tümü veya herhangi biri tamamlandığında bir devamlılık çağırın.
  • Zincir devamı, herhangi bir rastgele uzunlukta birbiri ardına devam eder.
  • Öncül tarafından oluşan özel durumları işlemek için bir devamlılık kullanın.

Devamlılıklar hakkında

Devamlılık, durumunda WaitingForActivation oluşturulan bir görevdir. Öncül görevi veya görevleri tamamlandığında otomatik olarak etkinleştirilir. Task.Start Kullanıcı kodunda devamlılık çağrısı bir System.InvalidOperationException özel durum oluşturur.

Devamlılık kendisidir Task ve başlatıldığı iş parçacığını engellemez. Task.Wait Devamlılık görevi bitene kadar engellemek için yöntemini çağırın.

Tek bir öncül için devamlılık oluşturma

Yöntemini çağırarak Task.ContinueWith öncül tamamlandığında yürütülen bir devamlılık oluşturursunuz. Aşağıdaki örnekte temel desen gösterilmektedir (netlik için özel durum işleme atlanmıştır). Haftanın geçerli gününün adını gösteren bir DayOfWeek nesne döndüren bir öncül görev taskA yürütür. taskA tamamlandığında, antecedent devamlılık yönteminde ContinueWith sonuçlarını temsil eder. Öncül görevin sonucu konsola yazılır.

using System;
using System.Threading.Tasks;

public class SimpleExample
{
    public static async Task Main()
    {
        // Declare, assign, and start the antecedent task.
        Task<DayOfWeek> taskA = Task.Run(() => DateTime.Today.DayOfWeek);

        // Execute the continuation when the antecedent finishes.
        await taskA.ContinueWith(antecedent => Console.WriteLine($"Today is {antecedent.Result}."));
    }
}
// The example displays the following output:
//       Today is Monday.
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        ' Execute the antecedent.
        Dim taskA As Task(Of DayOfWeek) = Task.Run(Function() DateTime.Today.DayOfWeek)

        ' Execute the continuation when the antecedent finishes.
        Dim continuation As Task = taskA.ContinueWith(Sub(antecedent)
                                                          Console.WriteLine("Today is {0}.", antecedent.Result)
                                                      End Sub)
        continuation.Wait()
    End Sub
End Module
' The example displays output like the following output:
'       Today is Monday.

Birden çok öncül için devamlılık oluşturma

Ayrıca, bir görev grubunun herhangi biri veya tümü tamamlandığında çalışacak bir devamlılık oluşturabilirsiniz. Tüm öncül görevler tamamlandığında devamı yürütmek için statik (Shared Visual Basic'te) Task.WhenAll yöntemini veya örnek TaskFactory.ContinueWhenAll yöntemini çağırabilirsiniz. Öncül görevlerden herhangi biri tamamlandığında devamı yürütmek için statik (Shared Visual Basic'te) Task.WhenAny yöntemini veya örnek TaskFactory.ContinueWhenAny yöntemini çağırabilirsiniz.

Task.WhenAll ve Task.WhenAny aşırı yüklemelerine yapılan çağrılar çağrı iş parçacığını engellemez. Ancak, çağrı iş parçacığını engelleyen döndürülen Task<TResult>.Result özelliği almak için genellikle ve Task.WhenAll(Task[]) yöntemleri dışında Task.WhenAll(IEnumerable<Task>) tümünü çağırırsınız.

Aşağıdaki örnek, 10 öncül görevinin sonuçlarını yansıtan bir devamlılık görevi oluşturmak için yöntemini çağırır Task.WhenAll(IEnumerable<Task>) . Her öncül görev, bir ile 10 arasında bir dizin değerinin karesini alır. Öncüller başarıyla tamamlanırsa (özellikleri Task.Status ), TaskStatus.RanToCompletionTask<TResult>.Result devamlılık özelliği her bir öncül tarafından döndürülen değerlerin Task<TResult>.Result bir dizisidir. Örnek, bir ile 10 arasındaki tüm sayıların karelerinin toplamını hesaplamak için bunları ekler:

using System.Collections.Generic;
using System;
using System.Threading.Tasks;

public class WhenAllExample
{
    public static async Task Main()
    {
        var tasks = new List<Task<int>>();
        for (int ctr = 1; ctr <= 10; ctr++)
        {
            int baseValue = ctr;
            tasks.Add(Task.Factory.StartNew(b => (int)b! * (int)b, baseValue));
        }

        var results = await Task.WhenAll(tasks);

        int sum = 0;
        for (int ctr = 0; ctr <= results.Length - 1; ctr++)
        {
            var result = results[ctr];
            Console.Write($"{result} {((ctr == results.Length - 1) ? "=" : "+")} ");
            sum += result;
        }

        Console.WriteLine(sum);
    }
}
// The example displays the similar output:
//    1 + 4 + 9 + 16 + 25 + 36 + 49 + 64 + 81 + 100 = 385
Imports System.Collections.Generic
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim tasks As New List(Of Task(Of Integer))()
        For ctr As Integer = 1 To 10
            Dim baseValue As Integer = ctr
            tasks.Add(Task.Factory.StartNew(Function(b)
                                                Dim i As Integer = CInt(b)
                                                Return i * i
                                            End Function, baseValue))
        Next
        Dim continuation = Task.WhenAll(tasks)

        Dim sum As Long = 0
        For ctr As Integer = 0 To continuation.Result.Length - 1
            Console.Write("{0} {1} ", continuation.Result(ctr),
                          If(ctr = continuation.Result.Length - 1, "=", "+"))
            sum += continuation.Result(ctr)
        Next
        Console.WriteLine(sum)
    End Sub
End Module
' The example displays the following output:
'       1 + 4 + 9 + 16 + 25 + 36 + 49 + 64 + 81 + 100 = 385

Devam seçenekleri

Tek görevli bir devamlılık oluşturduğunuzda, devamın başlatıldığı koşulları belirtmek için numaralandırma değeri alan bir System.Threading.Tasks.TaskContinuationOptions aşırı yükleme kullanabilirsinizContinueWith. Örneğin, devamlılık işleminin yalnızca öncül başarıyla tamamlanırsa veya yalnızca hatalı durumda tamamlanırsa çalıştırılacağını belirtebilirsiniz. Öncül devamlılığı çağırmaya hazır olduğunda koşul doğru değilse, devamlılık doğrudan TaskStatus.Canceled duruma geçirilir ve daha sonra başlatılamaz.

Yöntemin aşırı yüklemeleri TaskFactory.ContinueWhenAll gibi birçok çok görevli devamlılık yöntemi de bir System.Threading.Tasks.TaskContinuationOptions parametre içerir. Ancak, tüm System.Threading.Tasks.TaskContinuationOptions numaralandırma üyelerinin yalnızca bir alt kümesi geçerlidir. Numaralandırmada System.Threading.Tasks.TaskCreationOptions , ve TaskContinuationOptions.PreferFairnessgibi TaskContinuationOptions.LongRunningTaskContinuationOptions.AttachedToParentkarşılıkları olan değerleri belirtebilirsinizSystem.Threading.Tasks.TaskContinuationOptions. Veya OnlyOn seçeneklerinden herhangi birini NotOn çok görevli devamlılık ile belirtirseniz, çalışma zamanında bir ArgumentOutOfRangeException özel durum oluşturulur.

Görev devamlılığı seçenekleri hakkında daha fazla bilgi için makaleye TaskContinuationOptions bakın.

Devama veri geçirme

yöntemi, Task.ContinueWith devamlılık kullanıcı temsilcisine bağımsız değişken olarak bir başvuru geçirir. Öncül bir System.Threading.Tasks.Task<TResult> nesneyse ve görev tamamlanana kadar çalıştırıldıysa, devamlılık görevin özelliğine Task<TResult>.Result erişebilir.

Task<TResult>.Result Görev tamamlanana kadar özellik blokları. Ancak, görev iptal edildiyse veya hata oluştuysa, özelliğe erişmeye Result çalışmak bir AggregateException özel durum oluşturur. Aşağıdaki örnekte gösterildiği gibi seçeneğini kullanarak OnlyOnRanToCompletion bu sorundan kaçınabilirsiniz:

using System;
using System.Threading.Tasks;

public class ResultExample
{
    public static async Task Main()
    {
       var task = Task.Run(
           () =>
           {
                DateTime date = DateTime.Now;
                return date.Hour > 17
                    ? "evening"
                    : date.Hour > 12
                        ? "afternoon"
                        : "morning";
            });
        
        await task.ContinueWith(
            antecedent =>
            {
                Console.WriteLine($"Good {antecedent.Result}!");
                Console.WriteLine($"And how are you this fine {antecedent.Result}?");
            }, TaskContinuationOptions.OnlyOnRanToCompletion);
   }
}
// The example displays the similar output:
//       Good afternoon!
//       And how are you this fine afternoon?
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim t = Task.Run(Function()
                             Dim dat As DateTime = DateTime.Now
                             If dat = DateTime.MinValue Then
                                 Throw New ArgumentException("The clock is not working.")
                             End If

                             If dat.Hour > 17 Then
                                 Return "evening"
                             Else If dat.Hour > 12 Then
                                 Return "afternoon"
                             Else
                                 Return "morning"
                             End If
                         End Function)
        Dim c = t.ContinueWith(Sub(antecedent)
                                   Console.WriteLine("Good {0}!",
                                                     antecedent.Result)
                                   Console.WriteLine("And how are you this fine {0}?",
                                                     antecedent.Result)
                               End Sub, TaskContinuationOptions.OnlyOnRanToCompletion)
        c.Wait()
    End Sub
End Module
' The example displays output like the following:
'       Good afternoon!
'       And how are you this fine afternoon?

Öncül başarıyla tamamlanmamış olsa bile devamın çalışmasını istiyorsanız, özel duruma karşı korumanız gerekir. Yaklaşımlardan biri, öncül özelliğini test Task.Status etmek ve yalnızca durum veya Canceleddeğilse Faulted özelliğine erişmeye Result çalışmaktır. Ayrıca, öncül Exception özelliğini de inceleyebilirsiniz. Daha fazla bilgi için bkz . Özel Durum İşleme. Aşağıdaki örnek, önceki örneği yalnızca durumu TaskStatus.RanToCompletionolduğunda antecedent özelliğine Task<TResult>.Result erişecek şekilde değiştirir:

using System;
using System.Threading.Tasks;

public class ResultTwoExample
{
    public static async Task Main() =>
        await Task.Run(
            () =>
            {
                DateTime date = DateTime.Now;
                return date.Hour > 17
                   ? "evening"
                   : date.Hour > 12
                       ? "afternoon"
                       : "morning";
            })
            .ContinueWith(
                antecedent =>
                {
                    if (antecedent.Status == TaskStatus.RanToCompletion)
                    {
                        Console.WriteLine($"Good {antecedent.Result}!");
                        Console.WriteLine($"And how are you this fine {antecedent.Result}?");
                    }
                    else if (antecedent.Status == TaskStatus.Faulted)
                    {
                        Console.WriteLine(antecedent.Exception!.GetBaseException().Message);
                    }
                });
}
// The example displays output like the following:
//       Good afternoon!
//       And how are you this fine afternoon?
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim t = Task.Run(Function()
                             Dim dat As DateTime = DateTime.Now
                             If dat = DateTime.MinValue Then
                                 Throw New ArgumentException("The clock is not working.")
                             End If

                             If dat.Hour > 17 Then
                                 Return "evening"
                             Else If dat.Hour > 12 Then
                                 Return "afternoon"
                             Else
                                 Return "morning"
                             End If
                         End Function)
        Dim c = t.ContinueWith(Sub(antecedent)
                                   If t.Status = TaskStatus.RanToCompletion Then
                                       Console.WriteLine("Good {0}!",
                                                         antecedent.Result)
                                       Console.WriteLine("And how are you this fine {0}?",
                                                         antecedent.Result)
                                   Else If t.Status = TaskStatus.Faulted Then
                                       Console.WriteLine(t.Exception.GetBaseException().Message)
                                   End If
                               End Sub)
    End Sub
End Module
' The example displays output like the following:
'       Good afternoon!
'       And how are you this fine afternoon?

Devamı iptal etme

Bir Task.Status devamlılık özelliği aşağıdaki durumlarda olarak TaskStatus.Canceled ayarlanır:

Bir görev ve devamı aynı mantıksal işlemin iki parçasını temsil ederse, aşağıdaki örnekte gösterildiği gibi her iki göreve de aynı iptal belirtecini geçirebilirsiniz. 33 ile bölünebilen tamsayıların listesini oluşturan ve devama geçiren bir öncülden oluşur. Devamı sırayla listeyi görüntüler. Hem öncül hem de devamlılık rastgele aralıklar için düzenli olarak duraklatılır. Ayrıca, beş saniyelik zaman aşımı aralığından Elapsed sonra yöntemini yürütmek için bir System.Threading.Timer nesne kullanılır. Bu örnek yöntemini çağırır CancellationTokenSource.Cancel ve bu da şu anda yürütülmekte olan görevin yöntemini çağırmasına CancellationToken.ThrowIfCancellationRequested neden olur. Yöntemin CancellationTokenSource.Cancel öncül veya devamı yürütülürken çağrılıp çağrılmayacağı, rastgele oluşturulan duraklamaların süresine bağlıdır. Öncül iptal edilirse, devam başlatılmaz. Öncül iptal edilmediyse, devamı iptal etmek için belirteç hala kullanılabilir.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

public class CancellationExample
{
    static readonly Random s_random = new Random((int)DateTime.Now.Ticks);

    public static async Task Main()
    {
        using var cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;
        var timer = new Timer(Elapsed, cts, 5000, Timeout.Infinite);

        var task = Task.Run(
            async () =>
            {
                var product33 = new List<int>();
                for (int index = 1; index < short.MaxValue; index++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("\nCancellation requested in antecedent...\n");
                        token.ThrowIfCancellationRequested();
                    }
                    if (index % 2000 == 0)
                    {
                        int delay = s_random.Next(16, 501);
                        await Task.Delay(delay);
                    }
                    if (index % 33 == 0)
                    {
                        product33.Add(index);
                    }
                }

                return product33.ToArray();
            }, token);

        Task<double> continuation = task.ContinueWith(
            async antecedent =>
            {
                Console.WriteLine("Multiples of 33:\n");
                int[] array = antecedent.Result;
                for (int index = 0; index < array.Length; index++)
                {
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("\nCancellation requested in continuation...\n");
                        token.ThrowIfCancellationRequested();
                    }
                    if (index % 100 == 0)
                    {
                        int delay = s_random.Next(16, 251);
                        await Task.Delay(delay);
                    }

                    Console.Write($"{array[index]:N0}{(index != array.Length - 1 ? ", " : "")}");

                    if (Console.CursorLeft >= 74)
                    {
                        Console.WriteLine();
                    }
                }
                Console.WriteLine();
                return array.Average();
            }, token).Unwrap();

        try
        {
            await task;
            double result = await continuation;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

        Console.WriteLine("\nAntecedent Status: {0}", task.Status);
        Console.WriteLine("Continuation Status: {0}", continuation.Status);
    }

    static void Elapsed(object? state)
    {
        if (state is CancellationTokenSource cts)
        {
            cts.Cancel();
            Console.WriteLine("\nCancellation request issued...\n");
        }
    }
}
// The example displays the similar output:
//     Multiples of 33:
//     
//     33, 66, 99, 132, 165, 198, 231, 264, 297, 330, 363, 396, 429, 462, 495, 528,
//     561, 594, 627, 660, 693, 726, 759, 792, 825, 858, 891, 924, 957, 990, 1,023,
//     1,056, 1,089, 1,122, 1,155, 1,188, 1,221, 1,254, 1,287, 1,320, 1,353, 1,386,
//     1,419, 1,452, 1,485, 1,518, 1,551, 1,584, 1,617, 1,650, 1,683, 1,716, 1,749,
//     1,782, 1,815, 1,848, 1,881, 1,914, 1,947, 1,980, 2,013, 2,046, 2,079, 2,112,
//     2,145, 2,178, 2,211, 2,244, 2,277, 2,310, 2,343, 2,376, 2,409, 2,442, 2,475,
//     2,508, 2,541, 2,574, 2,607, 2,640, 2,673, 2,706, 2,739, 2,772, 2,805, 2,838,
//     2,871, 2,904, 2,937, 2,970, 3,003, 3,036, 3,069, 3,102, 3,135, 3,168, 3,201,
//     3,234, 3,267, 3,300, 3,333, 3,366, 3,399, 3,432, 3,465, 3,498, 3,531, 3,564,
//     3,597, 3,630, 3,663, 3,696, 3,729, 3,762, 3,795, 3,828, 3,861, 3,894, 3,927,
//     3,960, 3,993, 4,026, 4,059, 4,092, 4,125, 4,158, 4,191, 4,224, 4,257, 4,290,
//     4,323, 4,356, 4,389, 4,422, 4,455, 4,488, 4,521, 4,554, 4,587, 4,620, 4,653,
//     4,686, 4,719, 4,752, 4,785, 4,818, 4,851, 4,884, 4,917, 4,950, 4,983, 5,016,
//     5,049, 5,082, 5,115, 5,148, 5,181, 5,214, 5,247, 5,280, 5,313, 5,346, 5,379,
//     5,412, 5,445, 5,478, 5,511, 5,544, 5,577, 5,610, 5,643, 5,676, 5,709, 5,742,
//     Cancellation request issued...
//
//     5,775,
//     Cancellation requested in continuation...
//       
//     The operation was canceled.
//       
//     Antecedent Status: RanToCompletion
//     Continuation Status: Canceled
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim rnd As New Random()
        Dim lockObj As New Object()
        Dim cts As New CancellationTokenSource()
        Dim token As CancellationToken = cts.Token
        Dim timer As New Timer(AddressOf Elapsed, cts, 5000, Timeout.Infinite)

        Dim t = Task.Run(Function()
                             Dim product33 As New List(Of Integer)()
                             For ctr As Integer = 1 To Int16.MaxValue
                                 ' Check for cancellation.
                                 If token.IsCancellationRequested Then
                                     Console.WriteLine("\nCancellation requested in antecedent...\n")
                                     token.ThrowIfCancellationRequested()
                                 End If
                                 ' Introduce a delay.
                                 If ctr Mod 2000 = 0 Then
                                     Dim delay As Integer
                                     SyncLock lockObj
                                         delay = rnd.Next(16, 501)
                                     End SyncLock
                                     Thread.Sleep(delay)
                                 End If

                                 ' Determine if this is a multiple of 33.
                                 If ctr Mod 33 = 0 Then product33.Add(ctr)
                             Next
                             Return product33.ToArray()
                         End Function, token)

        Dim continuation = t.ContinueWith(Sub(antecedent)
                                              Console.WriteLine("Multiples of 33:" + vbCrLf)
                                              Dim arr = antecedent.Result
                                              For ctr As Integer = 0 To arr.Length - 1
                                                  If token.IsCancellationRequested Then
                                                      Console.WriteLine("{0}Cancellation requested in continuation...{0}",
                                                                        vbCrLf)
                                                      token.ThrowIfCancellationRequested()
                                                  End If

                                                  If ctr Mod 100 = 0 Then
                                                      Dim delay As Integer
                                                      SyncLock lockObj
                                                          delay = rnd.Next(16, 251)
                                                      End SyncLock
                                                      Thread.Sleep(delay)
                                                  End If
                                                  Console.Write("{0:N0}{1}", arr(ctr),
                                                                If(ctr <> arr.Length - 1, ", ", ""))
                                                  If Console.CursorLeft >= 74 Then Console.WriteLine()
                                              Next
                                              Console.WriteLine()
                                          End Sub, token)

        Try
            continuation.Wait()
        Catch e As AggregateException
            For Each ie In e.InnerExceptions
                Console.WriteLine("{0}: {1}", ie.GetType().Name,
                                  ie.Message)
            Next
        Finally
            cts.Dispose()
        End Try

        Console.WriteLine(vbCrLf + "Antecedent Status: {0}", t.Status)
        Console.WriteLine("Continuation Status: {0}", continuation.Status)
    End Sub

    Private Sub Elapsed(state As Object)
        Dim cts As CancellationTokenSource = TryCast(state, CancellationTokenSource)
        If cts Is Nothing Then return

        cts.Cancel()
        Console.WriteLine("{0}Cancellation request issued...{0}", vbCrLf)
    End Sub
End Module
' The example displays output like the following:
'    Multiples of 33:
'
'    33, 66, 99, 132, 165, 198, 231, 264, 297, 330, 363, 396, 429, 462, 495, 528,
'    561, 594, 627, 660, 693, 726, 759, 792, 825, 858, 891, 924, 957, 990, 1,023,
'    1,056, 1,089, 1,122, 1,155, 1,188, 1,221, 1,254, 1,287, 1,320, 1,353, 1,386,
'    1,419, 1,452, 1,485, 1,518, 1,551, 1,584, 1,617, 1,650, 1,683, 1,716, 1,749,
'    1,782, 1,815, 1,848, 1,881, 1,914, 1,947, 1,980, 2,013, 2,046, 2,079, 2,112,
'    2,145, 2,178, 2,211, 2,244, 2,277, 2,310, 2,343, 2,376, 2,409, 2,442, 2,475,
'    2,508, 2,541, 2,574, 2,607, 2,640, 2,673, 2,706, 2,739, 2,772, 2,805, 2,838,
'    2,871, 2,904, 2,937, 2,970, 3,003, 3,036, 3,069, 3,102, 3,135, 3,168, 3,201,
'    3,234, 3,267, 3,300, 3,333, 3,366, 3,399, 3,432, 3,465, 3,498, 3,531, 3,564,
'    3,597, 3,630, 3,663, 3,696, 3,729, 3,762, 3,795, 3,828, 3,861, 3,894, 3,927,
'    3,960, 3,993, 4,026, 4,059, 4,092, 4,125, 4,158, 4,191, 4,224, 4,257, 4,290,
'    4,323, 4,356, 4,389, 4,422, 4,455, 4,488, 4,521, 4,554, 4,587, 4,620, 4,653,
'    4,686, 4,719, 4,752, 4,785, 4,818, 4,851, 4,884, 4,917, 4,950, 4,983, 5,016,
'    5,049, 5,082, 5,115, 5,148, 5,181, 5,214, 5,247, 5,280, 5,313, 5,346, 5,379,
'    5,412, 5,445, 5,478, 5,511, 5,544, 5,577, 5,610, 5,643, 5,676, 5,709, 5,742,
'    5,775, 5,808, 5,841, 5,874, 5,907, 5,940, 5,973, 6,006, 6,039, 6,072, 6,105,
'    6,138, 6,171, 6,204, 6,237, 6,270, 6,303, 6,336, 6,369, 6,402, 6,435, 6,468,
'    6,501, 6,534, 6,567, 6,600, 6,633, 6,666, 6,699, 6,732, 6,765, 6,798, 6,831,
'    6,864, 6,897, 6,930, 6,963, 6,996, 7,029, 7,062, 7,095, 7,128, 7,161, 7,194,
'    7,227, 7,260, 7,293, 7,326, 7,359, 7,392, 7,425, 7,458, 7,491, 7,524, 7,557,
'    7,590, 7,623, 7,656, 7,689, 7,722, 7,755, 7,788, 7,821, 7,854, 7,887, 7,920,
'    7,953, 7,986, 8,019, 8,052, 8,085, 8,118, 8,151, 8,184, 8,217, 8,250, 8,283,
'    8,316, 8,349, 8,382, 8,415, 8,448, 8,481, 8,514, 8,547, 8,580, 8,613, 8,646,
'    8,679, 8,712, 8,745, 8,778, 8,811, 8,844, 8,877, 8,910, 8,943, 8,976, 9,009,
'    9,042, 9,075, 9,108, 9,141, 9,174, 9,207, 9,240, 9,273, 9,306, 9,339, 9,372,
'    9,405, 9,438, 9,471, 9,504, 9,537, 9,570, 9,603, 9,636, 9,669, 9,702, 9,735,
'    9,768, 9,801, 9,834, 9,867, 9,900, 9,933, 9,966, 9,999, 10,032, 10,065, 10,098,
'    10,131, 10,164, 10,197, 10,230, 10,263, 10,296, 10,329, 10,362, 10,395, 10,428,
'    10,461, 10,494, 10,527, 10,560, 10,593, 10,626, 10,659, 10,692, 10,725, 10,758,
'    10,791, 10,824, 10,857, 10,890, 10,923, 10,956, 10,989, 11,022, 11,055, 11,088,
'    11,121, 11,154, 11,187, 11,220, 11,253, 11,286, 11,319, 11,352, 11,385, 11,418,
'    11,451, 11,484, 11,517, 11,550, 11,583, 11,616, 11,649, 11,682, 11,715, 11,748,
'    11,781, 11,814, 11,847, 11,880, 11,913, 11,946, 11,979, 12,012, 12,045, 12,078,
'    12,111, 12,144, 12,177, 12,210, 12,243, 12,276, 12,309, 12,342, 12,375, 12,408,
'    12,441, 12,474, 12,507, 12,540, 12,573, 12,606, 12,639, 12,672, 12,705, 12,738,
'    12,771, 12,804, 12,837, 12,870, 12,903, 12,936, 12,969, 13,002, 13,035, 13,068,
'    13,101, 13,134, 13,167, 13,200, 13,233, 13,266,
'    Cancellation requested in continuation...
'
'
'    Cancellation request issued...
'
'    TaskCanceledException: A task was canceled.
'
'    Antecedent Status: RanToCompletion
'    Continuation Status: Canceled

Ayrıca, bir devamlılık belirteci sağlamadan öncül iptal edilirse devamın yürütülmesini engelleyebilirsiniz. Aşağıdaki örnekte gösterildiği gibi devamlılığı oluştururken seçeneğini belirterek TaskContinuationOptions.NotOnCanceled belirteci sağlayın:

using System;
using System.Threading;
using System.Threading.Tasks;

public class CancellationTwoExample
{
    public static async Task Main()
    {
        using var cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;
        cts.Cancel();

        var task = Task.FromCanceled(token);
        Task continuation =
            task.ContinueWith(
                antecedent => Console.WriteLine("The continuation is running."),
                TaskContinuationOptions.NotOnCanceled);

        try
        {
            await task;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
            Console.WriteLine();
        }

        Console.WriteLine($"Task {task.Id}: {task.Status:G}");
        Console.WriteLine($"Task {continuation.Id}: {continuation.Status:G}");
    }
}
// The example displays the similar output:
//       TaskCanceledException: A task was canceled.
//
//       Task 1: Canceled
//       Task 2: Canceled
Imports System.Threading
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim cts As New CancellationTokenSource()
        Dim token As CancellationToken = cts.Token
        cts.Cancel()

        Dim t As Task = Task.FromCanceled(token)
        Dim continuation As Task = t.ContinueWith(Sub(antecedent)
                                                      Console.WriteLine("The continuation is running.")
                                                  End Sub, TaskContinuationOptions.NotOnCanceled)
        Try
            t.Wait()
        Catch e As AggregateException
            For Each ie In e.InnerExceptions
                Console.WriteLine("{0}: {1}", ie.GetType().Name, ie.Message)
            Next
            Console.WriteLine()
        Finally
            cts.Dispose()
        End Try

        Console.WriteLine("Task {0}: {1:G}", t.Id, t.Status)
        Console.WriteLine("Task {0}: {1:G}", continuation.Id,
                          continuation.Status)
    End Sub
End Module
' The example displays the following output:
'       TaskCanceledException: A task was canceled.
'
'       Task 1: Canceled
'       Task 2: Canceled

Bir devamlılık duruma geçtikten Canceled sonra, bu devamlılıklar için belirtilene bağlı TaskContinuationOptions olarak izleyen devamlılıkları etkileyebilir.

Atılan devamlılıklar başlatılmaz.

Devamlılıklar ve alt görevler

Bir devamlılık, öncül ve ekli tüm alt görevleri tamamlanana kadar çalışmaz. Devam, ayrılmış alt görevlerin tamamlanmasını beklemez. Aşağıdaki iki örnek, bir devamlılık oluşturan bir öncüle eklenmiş ve ondan ayrılmış alt görevleri göstermektedir. Aşağıdaki örnekte, devamlılık yalnızca tüm alt görevler tamamlandıktan sonra çalıştırılır ve örneğin birden çok çalıştırması her seferinde aynı çıkışı üretir. Varsayılan olarak Task.Run yöntemi varsayılan görev oluşturma seçeneği TaskCreationOptions.DenyChildAttacholan bir üst görev oluşturduğundan örnek, yöntemini çağırarak TaskFactory.StartNew öncül'leri başlatır.

using System;
using System.Threading.Tasks;

public class AttachedExample
{
    public static async Task Main()
    {
        await Task.Factory
                  .StartNew(
            () =>
            {
                Console.WriteLine($"Running antecedent task {Task.CurrentId}...");
                Console.WriteLine("Launching attached child tasks...");
                for (int ctr = 1; ctr <= 5; ctr++)
                {
                    int index = ctr;
                    Task.Factory.StartNew(async value =>
                    {
                        Console.WriteLine($"   Attached child task #{value} running");
                        await Task.Delay(1000);
                    }, index, TaskCreationOptions.AttachedToParent);
                }
                Console.WriteLine("Finished launching attached child tasks...");
            }).ContinueWith(
                antecedent =>
                    Console.WriteLine($"Executing continuation of Task {antecedent.Id}"));
    }
}
// The example displays the similar output:
//     Running antecedent task 1...
//     Launching attached child tasks...
//     Finished launching attached child tasks...
//        Attached child task #1 running
//        Attached child task #5 running
//        Attached child task #3 running
//        Attached child task #2 running
//        Attached child task #4 running
//     Executing continuation of Task 1
Imports System.Threading
Imports System.Threading.Tasks

Public Module Example
    Public Sub Main()
        Dim t = Task.Factory.StartNew(Sub()
                                          Console.WriteLine("Running antecedent task {0}...",
                                                            Task.CurrentId)
                                          Console.WriteLine("Launching attached child tasks...")
                                          For ctr As Integer = 1 To 5
                                              Dim index As Integer = ctr
                                              Task.Factory.StartNew(Sub(value)
                                                                        Console.WriteLine("   Attached child task #{0} running",
                                                                                          value)
                                                                        Thread.Sleep(1000)
                                                                    End Sub, index, TaskCreationOptions.AttachedToParent)
                                          Next
                                          Console.WriteLine("Finished launching attached child tasks...")
                                      End Sub)
        Dim continuation = t.ContinueWith(Sub(antecedent)
                                              Console.WriteLine("Executing continuation of Task {0}",
                                                                antecedent.Id)
                                          End Sub)
        continuation.Wait()
    End Sub
End Module
' The example displays the following output:
'       Running antecedent task 1...
'       Launching attached child tasks...
'       Finished launching attached child tasks...
'          Attached child task #5 running
'          Attached child task #1 running
'          Attached child task #2 running
'          Attached child task #3 running
'          Attached child task #4 running
'       Executing continuation of Task 1

Ancak alt görevler öncülden ayrılırsa, alt görevlerin durumundan bağımsız olarak, devamlılık, öncül sonlandırıldığı anda çalışır. Sonuç olarak, aşağıdaki örneğin birden çok çalıştırması, görev zamanlayıcının her alt görevi nasıl işlediğine bağlı olarak değişken çıkış oluşturabilir:

using System;
using System.Threading.Tasks;

public class DetachedExample
{
    public static async Task Main()
    {
        Task task =
            Task.Factory.StartNew(
                () =>
                {
                    Console.WriteLine($"Running antecedent task {Task.CurrentId}...");
                    Console.WriteLine("Launching attached child tasks...");
                    for (int ctr = 1; ctr <= 5; ctr++)
                    {
                        int index = ctr;
                        Task.Factory.StartNew(
                            async value =>
                            {
                                Console.WriteLine($"   Attached child task #{value} running");
                                await Task.Delay(1000);
                            }, index);
                    }
                    Console.WriteLine("Finished launching detached child tasks...");
                }, TaskCreationOptions.DenyChildAttach);

        Task continuation =
            task.ContinueWith(
                antecedent =>
                    Console.WriteLine($"Executing continuation of Task {antecedent.Id}"));

        await continuation;

        Console.ReadLine();
    }
}
// The example displays the similar output:
//     Running antecedent task 1...
//     Launching attached child tasks...
//     Finished launching detached child tasks...
//     Executing continuation of Task 1
//        Attached child task #1 running
//        Attached child task #5 running
//        Attached child task #2 running
//        Attached child task #3 running
//        Attached child task #4 running
Imports System.Threading
Imports System.Threading.Tasks

Public Module Example
    Public Sub Main()
        Dim t = Task.Factory.StartNew(Sub()
                                          Console.WriteLine("Running antecedent task {0}...",
                                                            Task.CurrentId)
                                          Console.WriteLine("Launching attached child tasks...")
                                          For ctr As Integer = 1 To 5
                                              Dim index As Integer = ctr
                                              Task.Factory.StartNew(Sub(value)
                                                                        Console.WriteLine("   Attached child task #{0} running",
                                                                                          value)
                                                                        Thread.Sleep(1000)
                                                                    End Sub, index)
                                          Next
                                          Console.WriteLine("Finished launching detached child tasks...")
                                      End Sub, TaskCreationOptions.DenyChildAttach)
        Dim continuation = t.ContinueWith(Sub(antecedent)
                                              Console.WriteLine("Executing continuation of Task {0}",
                                                                antecedent.Id)
                                          End Sub)
        continuation.Wait()
    End Sub
End Module
' The example displays output like the following:
'       Running antecedent task 1...
'       Launching attached child tasks...
'       Finished launching detached child tasks...
'          Attached child task #1 running
'          Attached child task #2 running
'          Attached child task #5 running
'          Attached child task #3 running
'       Executing continuation of Task 1
'          Attached child task #4 running

Öncül görevin son durumu, ekli alt görevlerin son durumuna bağlıdır. Ayrılmış alt görevlerin durumu üst öğeyi etkilemez. Daha fazla bilgi için bkz . Ekli ve Ayrılmış Alt Görevler.

Durumu devamlılıklarla ilişkilendirme

Rastgele durumu bir görev devamlılığı ile ilişkilendirebilirsiniz. yöntemi, ContinueWith her birinin devamlılık durumunu temsil eden bir Object değer alan aşırı yüklenmiş sürümler sağlar. Daha sonra özelliğini kullanarak bu durum nesnesine Task.AsyncState erişebilirsiniz. Bu durum nesnesi, null bir değer sağlamazsanız olur.

Devamlılık durumu, Zaman Uyumsuz Programlama Modeli'ni (APM) kullanan mevcut kodu TPL'yi kullanacak şekilde dönüştürdüğünüzde kullanışlıdır. APM'de BeginYöntemi yönteminde nesne durumu sağlayabilir ve daha sonra bu duruma erişmek için özelliğini kullanabilirsinizIAsyncResult.AsyncState. TPL'yi kullanmak üzere APM kullanan bir kodu dönüştürürken bu durumu korumak için yöntemini kullanırsınız ContinueWith .

Devam durumu, Visual Studio hata ayıklayıcısında nesnelerle Task çalışırken de yararlı olabilir. Örneğin, Paralel Görevler penceresinde Görev sütunu her görev için durum nesnesinin dize gösterimini görüntüler. Paralel Görevler penceresi hakkında daha fazla bilgi için bkz. Görevler Penceresini Kullanma.

Aşağıdaki örnekte devamlılık durumunun nasıl kullanılacağı gösterilmektedir. Bir devamlılık görevleri zinciri oluşturur. Her görev, yönteminin parametresi için state geçerli saati ( bir DateTime nesnesi) ContinueWith sağlar. Her DateTime nesne, devamlılık görevinin oluşturulduğu saati temsil eder. Her görev, sonucu olarak görevin bitiş zamanını temsil eden ikinci DateTime bir nesne oluşturur. Tüm görevler tamamlandıktan sonra, bu örnekte oluşturma zamanı ve her devamlılık görevinin bitiş saati görüntülenir.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

class ContinuationStateExample
{
    static DateTime DoWork()
    {
        Thread.Sleep(2000);

        return DateTime.Now;
    }

    static async Task Main()
    {
        Task<DateTime> task = Task.Run(() => DoWork());

        var continuations = new List<Task<DateTime>>();
        for (int i = 0; i < 5; i++)
        {
            task = task.ContinueWith((antecedent, _) => DoWork(), DateTime.Now);
            continuations.Add(task);
        }

        await task;

        foreach (Task<DateTime> continuation in continuations)
        {
            DateTime start = (DateTime)continuation.AsyncState!;
            DateTime end = continuation.Result;

            Console.WriteLine($"Task was created at {start.TimeOfDay} and finished at {end.TimeOfDay}.");
        }

        Console.ReadLine();
    }
}
// The example displays the similar output:
//     Task was created at 10:56:21.1561762 and finished at 10:56:25.1672062.
//     Task was created at 10:56:21.1610677 and finished at 10:56:27.1707646.
//     Task was created at 10:56:21.1610677 and finished at 10:56:29.1743230.
//     Task was created at 10:56:21.1610677 and finished at 10:56:31.1779883.
//     Task was created at 10:56:21.1610677 and finished at 10:56:33.1837083.
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

' Demonstrates how to associate state with task continuations.
Public Module ContinuationState
    ' Simulates a lengthy operation and returns the time at which
    ' the operation completed.
    Public Function DoWork() As Date
        ' Simulate work by suspending the current thread
        ' for two seconds.
        Thread.Sleep(2000)

        ' Return the current time.
        Return Date.Now
    End Function

    Public Sub Main()
        ' Start a root task that performs work.
        Dim t As Task(Of Date) = Task(Of Date).Run(Function() DoWork())

        ' Create a chain of continuation tasks, where each task is
        ' followed by another task that performs work.
        Dim continuations As New List(Of Task(Of DateTime))()
        For i As Integer = 0 To 4
            ' Provide the current time as the state of the continuation.
            t = t.ContinueWith(Function(antecedent, state) DoWork(), DateTime.Now)
            continuations.Add(t)
        Next

        ' Wait for the last task in the chain to complete.
        t.Wait()

        ' Display the creation time of each continuation (the state object)
        ' and the completion time (the result of that task) to the console.
        For Each continuation In continuations
            Dim start As DateTime = CDate(continuation.AsyncState)
            Dim [end] As DateTime = continuation.Result

            Console.WriteLine("Task was created at {0} and finished at {1}.",
               start.TimeOfDay, [end].TimeOfDay)
        Next
    End Sub
End Module
' The example displays output like the following:
'       Task was created at 10:56:21.1561762 and finished at 10:56:25.1672062.
'       Task was created at 10:56:21.1610677 and finished at 10:56:27.1707646.
'       Task was created at 10:56:21.1610677 and finished at 10:56:29.1743230.
'       Task was created at 10:56:21.1610677 and finished at 10:56:31.1779883.
'       Task was created at 10:56:21.1610677 and finished at 10:56:33.1837083.

Görev türlerini döndüren devamlılıklar

Bazen bir tür döndüren Task bir devamlılığı zincirlemeniz gerekebilir. Bu görevler iç içe görevler olarak adlandırılır. Üst görev çağırdığında Task<TResult>.ContinueWith ve görev döndüren bir continuationFunction görev sağladığında, veya Task(Of Task(Of T)) (Visual Basic) zaman uyumsuz işlemini <Task<Task<T>>> temsil eden bir ara sunucu görevi oluşturmak için çağırabilirsinizUnwrap.

Aşağıdaki örnekte, ek görev döndüren işlevleri sarmalayan devamlılıkların nasıl kullanılacağı gösterilmektedir. Her devamlılık, sarmalanan iç görevi ortaya çıkararak açılabilir.

using System;
using System.Threading;
using System.Threading.Tasks;

public class UnwrapExample
{
    public static async Task Main()
    {
        Task<int> taskOne = RemoteIncrement(0);
        Console.WriteLine("Started RemoteIncrement(0)");

        Task<int> taskTwo = RemoteIncrement(4)
            .ContinueWith(t => RemoteIncrement(t.Result))
            .Unwrap().ContinueWith(t => RemoteIncrement(t.Result))
            .Unwrap().ContinueWith(t => RemoteIncrement(t.Result))
            .Unwrap();

        Console.WriteLine("Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)");

        try
        {
            await taskOne;
            Console.WriteLine("Finished RemoteIncrement(0)");

            await taskTwo;
            Console.WriteLine("Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)");
        }
        catch (Exception e)
        {
            Console.WriteLine($"A task has thrown the following (unexpected) exception:\n{e}");
        }
    }

    static Task<int> RemoteIncrement(int number) =>
        Task<int>.Factory.StartNew(
            obj =>
            {
                Thread.Sleep(1000);

                int x = (int)(obj!);
                Console.WriteLine("Thread={0}, Next={1}", Thread.CurrentThread.ManagedThreadId, ++x);
                return x;
            },
            number);
}

// The example displays the similar output:
//     Started RemoteIncrement(0)
//     Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
//     Thread=4, Next=1
//     Finished RemoteIncrement(0)
//     Thread=5, Next=5
//     Thread=6, Next=6
//     Thread=6, Next=7
//     Thread=6, Next=8
//     Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
Imports System.Threading

Module UnwrapExample
    Sub Main()
        Dim taskOne As Task(Of Integer) = RemoteIncrement(0)
        Console.WriteLine("Started RemoteIncrement(0)")

        Dim taskTwo As Task(Of Integer) = RemoteIncrement(4).
            ContinueWith(Function(t) RemoteIncrement(t.Result)).
            Unwrap().ContinueWith(Function(t) RemoteIncrement(t.Result)).
            Unwrap().ContinueWith(Function(t) RemoteIncrement(t.Result)).
            Unwrap()

        Console.WriteLine("Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)")

        Try
            taskOne.Wait()
            Console.WriteLine("Finished RemoteIncrement(0)")

            taskTwo.Wait()
            Console.WriteLine("Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)")
        Catch e As AggregateException
            Console.WriteLine($"A task has thrown the following (unexpected) exception:{vbLf}{e}")
        End Try
    End Sub

    Function RemoteIncrement(ByVal number As Integer) As Task(Of Integer)
        Return Task(Of Integer).Factory.StartNew(
            Function(obj)
                Thread.Sleep(1000)

                Dim x As Integer = CInt(obj)
                Console.WriteLine("Thread={0}, Next={1}", Thread.CurrentThread.ManagedThreadId, Interlocked.Increment(x))
                Return x
            End Function, number)
    End Function
End Module

' The example displays the similar output:
'     Started RemoteIncrement(0)
'     Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)
'     Thread=4, Next=1
'     Finished RemoteIncrement(0)
'     Thread=5, Next=5
'     Thread=6, Next=6
'     Thread=6, Next=7
'     Thread=6, Next=8
'     Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)

kullanma Unwraphakkında daha fazla bilgi için bkz . Nasıl yapılır: İç içe görev sarmayı kaldırma.

Devamlılıklardan oluşan özel durumları işleme

Antecedent-continuation ilişkisi bir üst-alt ilişki değildir. Devamlılıklar tarafından oluşan özel durumlar, öncül öğeye yayılmaz. Bu nedenle, devamlılıklar tarafından oluşturulan özel durumları aşağıdaki gibi başka bir görevde işleyecek şekilde işleyin:

  • Devamı Waitbeklemek için , WaitAll, veya WaitAny yöntemini veya genel karşılık gelenini kullanabilirsiniz. Aşağıdaki örnekte gösterildiği gibi, aynı try deyimde bir öncül ve devamını bekleyebilirsiniz:
using System;
using System.Threading.Tasks;

public class ExceptionExample
{
    public static async Task Main()
    {
        Task<int> task = Task.Run(
            () =>
            {
                Console.WriteLine($"Executing task {Task.CurrentId}");
                return 54;
            });

        var continuation = task.ContinueWith(
            antecedent =>
            {
                Console.WriteLine($"Executing continuation task {Task.CurrentId}");
                Console.WriteLine($"Value from antecedent: {antecedent.Result}");

                throw new InvalidOperationException();
            });

        try
        {
            await task;
            await continuation;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
// The example displays the similar output:
//       Executing task 1
//       Executing continuation task 2
//       Value from antecedent: 54
//       Operation is not valid due to the current state of the object.
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim task1 = Task(Of Integer).Run(Function()
                                             Console.WriteLine("Executing task {0}",
                                                               Task.CurrentId)
                                             Return 54
                                         End Function)
        Dim continuation = task1.ContinueWith(Sub(antecedent)
                                                  Console.WriteLine("Executing continuation task {0}",
                                                                    Task.CurrentId)
                                                  Console.WriteLine("Value from antecedent: {0}",
                                                                    antecedent.Result)
                                                  Throw New InvalidOperationException()
                                              End Sub)

        Try
            task1.Wait()
            continuation.Wait()
        Catch ae As AggregateException
            For Each ex In ae.InnerExceptions
                Console.WriteLine(ex.Message)
            Next
        End Try
    End Sub
End Module
' The example displays the following output:
'       Executing task 1
'       Executing continuation task 2
'       Value from antecedent: 54
'       Operation is not valid due to the current state of the object.
  • İlk devamlılık özelliğini gözlemlemek Exception için ikinci bir devamlılık kullanabilirsiniz. Aşağıdaki örnekte, bir görev var olmayan bir dosyadan okumaya çalışır. Devam daha sonra öncül görevdeki özel durum hakkındaki bilgileri görüntüler.
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

public class ExceptionTwoExample
{
    public static async Task Main()
    {
        var task = Task.Run(
            () =>
            {
                string fileText = File.ReadAllText(@"C:\NonexistentFile.txt");
                return fileText;
            });

        Task continuation = task.ContinueWith(
            antecedent =>
            {
                var fileNotFound =
                    antecedent.Exception
                        ?.InnerExceptions
                        ?.FirstOrDefault(e => e is FileNotFoundException) as FileNotFoundException;

                if (fileNotFound != null)
                {
                    Console.WriteLine(fileNotFound.Message);
                }
            }, TaskContinuationOptions.OnlyOnFaulted);

        await continuation;

        Console.ReadLine();
    }
}
// The example displays the following output:
//        Could not find file 'C:\NonexistentFile.txt'.
Imports System.IO
Imports System.Threading.Tasks

Module Example
    Public Sub Main()
        Dim t = Task.Run(Function()
                             Dim s As String = File.ReadAllText("C:\NonexistentFile.txt")
                             Return s
                         End Function)

        Dim c = t.ContinueWith(Sub(antecedent)
                                   ' Get the antecedent's exception information.
                                   For Each ex In antecedent.Exception.InnerExceptions
                                       If TypeOf ex Is FileNotFoundException
                                           Console.WriteLine(ex.Message)
                                       End If
                                   Next
                               End Sub, TaskContinuationOptions.OnlyOnFaulted)

        c.Wait()
    End Sub
End Module
' The example displays the following output:
'       Could not find file 'C:\NonexistentFile.txt'.

seçeneğiyle çalıştırıldığından TaskContinuationOptions.OnlyOnFaulted , devamlılık yalnızca öncülde bir özel durum oluştuğunda yürütülür. Bu nedenle, öncül Exception özelliğinin olmadığını nullvarsayabilir. Devamlılık, öncülde bir özel durum oluşturup oluşturmadığını yürütürse, aşağıdaki kod parçasında gösterildiği gibi, özel durumu işlemeye çalışmadan önce öncül Exception özelliğinin olup olmadığını null denetlemesi gerekir:

var fileNotFound =
    antecedent.Exception
        ?.InnerExceptions
        ?.FirstOrDefault(e => e is FileNotFoundException) as FileNotFoundException;

if (fileNotFound != null)
{
    Console.WriteLine(fileNotFound.Message);
}
' Determine whether an exception occurred.
If antecedent.Exception IsNot Nothing Then
    ' Get the antecedent's exception information.
    For Each ex In antecedent.Exception.InnerExceptions
        If TypeOf ex Is FileNotFoundException
            Console.WriteLine(ex.Message)
        End If
    Next
End If

Daha fazla bilgi için bkz . Özel Durum İşleme.

Ayrıca bkz.