Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
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, devamlar geri çağırma yöntemleri kullanılarak yapıldı. 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 tamamlandığında öncülolarak bilinen başka bir görev tarafından ç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 sonraki aşamaya veri geçirme.
- Devamın çağrılacağı veya çağrılmayacak olduğu kesin koşulları belirtin.
- Bir işlemi başlamadan önce veya çalışırken uyum içinde 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 uzantıları, istediğiniz uzunlukta arka arkaya devam edebilir.
- Öncül tarafından oluşturulan istisnaları işlemek için bir devamlılık kullanın.
Devamlılıklar hakkında
Devam görevi, WaitingForActivation durumunda oluşturulmuş bir görevdir. Öncül görevi veya görevleri tamamlandığında otomatik olarak etkinleştirilir. Kullanıcı kodunda devamlılıkta Task.Start çağrıldığında System.InvalidOperationException istisnası oluşur.
Devam kendisi bir Task'dur ve üzerinde başlatıldığı iş parçacığını engellemez. Devam görevi bitene kadar engellemek için Task.Wait yöntemini çağırın.
Tek bir öncül için devamlılık oluşturma
Task.ContinueWith yöntemini çağırarak öncül tamamlandığında yürütülen bir devamlılık oluşturursunuz. Aşağıdaki örnekte temel model gösterilmektedir (netlik için istisna işleme atlanmıştır). Haftanın geçerli gününün adını gösteren bir taskA nesnesi döndüren bir öncül görev DayOfWeek yürütür.
taskA tamamlandığında, antecedent sonuçlarını ContinueWith devamlılık yönteminde 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 (Visual Basic'teShared) 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 (Visual Basic'teShared) Task.WhenAny yöntemini veya örnek TaskFactory.ContinueWhenAny yöntemini çağırabilirsiniz.
Task.WhenAll ve Task.WhenAny aşırı yüklenmelerine yapılan çağrılar çağrı iş parçacığını engellemez. Ancak, genellikle Task.WhenAll(IEnumerable<Task>) ve Task.WhenAll(Task[]) yöntemleri haricinde, çağrılan iş parçacığını engelleyen döndürülen Task<TResult>.Result özelliğini almak için diğer yöntemlerin 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 Task.WhenAll(IEnumerable<Task>) yöntemini çağırır. Her öncül görev, bir ile 10 arasında bir dizin değerinin karesini hesaplar. Öncüller başarıyla tamamlandığında (Task.Status özellikleri TaskStatus.RanToCompletion), sürdürülen işlemin Task<TResult>.Result özelliği, her öncül tarafından döndürülen Task<TResult>.Result değerlerini içeren bir dizidir. Ö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ılma koşullarını belirtmek için bir ContinueWith numaralandırma değeri alan bir System.Threading.Tasks.TaskContinuationOptions aşırı yüklemesi kullanabilirsiniz. Ö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 durumuna geçirilir ve daha sonra başlatılamaz.
TaskFactory.ContinueWhenAll yönteminin aşırı yüklemeleri gibi birçok çok görevli devamlılık yöntemi de bir System.Threading.Tasks.TaskContinuationOptions parametresi içerir. Ancak, tüm System.Threading.Tasks.TaskContinuationOptions numaralandırma üyelerinin yalnızca bir alt kümesi geçerlidir.
System.Threading.Tasks.TaskContinuationOptions, System.Threading.Tasks.TaskCreationOptionsve TaskContinuationOptions.AttachedToParentgibi TaskContinuationOptions.LongRunning numaralandırmasında karşılıkları olan TaskContinuationOptions.PreferFairness değerleri belirtebilirsiniz.
NotOn veya OnlyOn seçeneklerinden herhangi birini çok görevli bir devamlılık ile belirtirseniz, çalışma zamanında bir ArgumentOutOfRangeException özel durumu atılacaktır.
Görev devamlılığı seçenekleri hakkında daha fazla bilgi için TaskContinuationOptions makalesine bakın.
Devama veri geçirme
Task.ContinueWith yöntemi, devamın kullanıcı temsilcisine bağımsız değişken olarak bir başvuru geçirir. Öncül bir System.Threading.Tasks.Task<TResult> nesnesiyse ve görev tamamlanana kadar çalıştırıldıysa, devamlılık görevin Task<TResult>.Result özelliğine erişebilir.
görev tamamlanana kadar Task<TResult>.Result özelliği engeller. Ancak, görev iptal edildiyse veya hatalıysa, Result özelliğine erişmeye çalışmak bir AggregateException özel durumu oluşturur. Aşağıdaki örnekte gösterildiği gibi OnlyOnRanToCompletion seçeneğini kullanarak 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 sorunsuz çalışmasını istiyorsanız, istisnalara karşı önlem almanız gerekir. Yaklaşımlardan biri, öncül öğesinin Task.Status özelliğini test etmek ve yalnızca durum Result veya Faulteddeğilse Canceled özelliğine erişmeye çalışmaktır. Ayrıca, öncül öğesinin Exception özelliğini de inceleyebilirsiniz. Daha fazla bilgi için bkz. Özel Durum İşleme. Aşağıdaki örnek, önceki örneği yalnızca durumu Task<TResult>.Resultolduğunda antecedent'in TaskStatus.RanToCompletion özelliğine 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 et
Bir devamlılığın Task.Status özelliği, aşağıdaki durumlarda TaskStatus.Canceled olarak ayarlanır:
İptal isteğine yanıt olarak bir OperationCanceledException özel durumu oluşturur. Herhangi bir görevde olduğu gibi, istisna, devam ettirmeye geçirilen belirteçle aynı belirteci içeriyorsa, bu işbirlikçi bir iptal olarak kabul edilir.
Süreklilik, System.Threading.CancellationToken özelliği IsCancellationRequestedolan bir
true'a aktarılır. Bu durumda devam edilmez ve TaskStatus.Canceled durumuna geçiş yapılır.TaskContinuationOptions bağımsız değişkeni tarafından ayarlanan koşul karşılanmadığından, devam eden işlem hiçbir zaman çalışmaz. Örneğin, bir öncül TaskStatus.Faulted durumuna geçerse, TaskContinuationOptions.NotOnFaulted seçeneğine geçirilen devamı çalışmaz ancak Canceled durumuna geçer.
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 önceki hem de devam eden, rastgele aralıklarda düzenli olarak duraklar. Ayrıca, beş saniyelik zaman aşımı aralığından sonra System.Threading.Timer yöntemini yürütmek için bir Elapsed nesnesi kullanılır. Bu örnek, şu anda yürütülen görevin CancellationTokenSource.Cancel yöntemini çağırmasına neden olan CancellationToken.ThrowIfCancellationRequested yöntemini çağırır.
CancellationTokenSource.Cancel yönteminin ö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: {task.Status}");
Console.WriteLine($"Continuation Status: {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 iptal 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 TaskContinuationOptions.NotOnCanceled seçeneğini belirterek 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 Canceled durumuna geçtikten sonra, bu devamlılıklar için belirtilen TaskContinuationOptions bağlı olarak izleyen devamları etkileyebilir.
İşlemden kaldırılan devamlılıklar başlatılmaz.
Devamlılıklar ve alt görevler
Devam eden bir görev, önceki görev ve ona bağlı tüm alt görevler tamamlanana kadar çalışmaz. Devam, ayrılmış alt görevlerin tamamlanmasını beklemez. Aşağıdaki iki örnek, bir devamlılık oluşturan öncül göreve bağlanan ve ondan ayrılan alt görevleri göstermektedir. Aşağıdaki örnekte, devam yalnızca tüm alt görevler tamamlandıktan sonra çalışır ve örneğin birden çok kez çalıştırıldığında her seferinde aynı çıkışı üretir. Varsayılan olarak TaskFactory.StartNew yöntemi varsayılan görev oluşturma seçeneği Task.Runolan bir üst görev oluşturduğundan, örnek TaskCreationOptions.DenyChildAttach yöntemini çağırarak öncül 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ırılması, görev zamanlayıcının her bir alt görevi nasıl işlediğine bağlı olarak farklı çıktılar üretebilir.
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, herhangi bir bağlı alt görevin son durumuna bağlıdır. Ayrılmış alt görevlerin durumu ana görevi 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.
ContinueWith yöntemi, her biri süreç devamının durumunu gösteren bir Object değeri alan aşırı yüklenmiş sürümler sağlar. Daha sonra Task.AsyncState özelliğini kullanarak bu durum nesnesine erişebilirsiniz. Bir değer sağlamazsanız, bu durum nesnesinin değeri null olur.
Devamlılık durumu, Zaman Uyumsuz Programlama Modeli (APM) kullanan mevcut kodu TPL kullanmak üzere dönüştürdüğünüzde kullanışlıdır. APM'de, BeginMetodu yönteminde nesne durumu sağlayabilir ve daha sonra bu duruma erişmek için IAsyncResult.AsyncState özelliğini kullanabilirsiniz. TPL'yi kullanmak üzere APM kullanan bir kodu dönüştürürken bu durumu korumak için ContinueWith yöntemini kullanırsınız.
Devam durumu, Visual Studio hata ayıklayıcısında Task nesnelerle ç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 bir görev, DateTime yönteminin state parametresi için güncel saati ContinueWith nesnesi sağlar. Her DateTime nesnesi, devamlılık görevinin oluşturulduğu saati temsil eder. Her görev, sonucu olarak, görevin bitiş saatini temsil eden ikinci bir DateTime nesnesi 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.
Task türlerini döndüren devamlılıklar
Bazen Task türü döndüren bir devamlılığı zincirlemeniz gerekebilir. Bu görevler iç içe görevler olarak adlandırılır. Ana görev Task<TResult>.ContinueWith çağırdığında ve görev döndüren bir continuationFunction sağladığında, Unwrap veya <Task<Task<T>>> (Visual Basic) zaman uyumsuz işlemini temsil eden bir vekil görev oluşturmak için Task(Of Task(Of T)) çağırabilirsiniz.
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))...)
Nasıl yapılır: Unwrapkullanma hakkında daha fazla bilgi için, iç içe geçmiş bir Görevi Açma konusuna bakın.
Devamlılıklardan oluşan özel durumları işleme
Öncül-devam ilişkisi bir ebeveyn-çocuk ilişkisi değildir. Devamlar tarafından oluşturulan istisnalar, ö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ı beklemek için Wait, WaitAllveya WaitAny yöntemini ya da genel karşılık gelenini kullanabilirsiniz. Aşağıdaki örnekte gösterildiği gibi, aynı
trydeyiminde bir öncül ve onun devamları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ığın Exception özelliğini gözlemlemek için ikinci devamlılığı kullanabilirsiniz. Aşağıdaki örnekte, bir görev var olmayan bir dosyadan okumaya çalışır. Devamında, öncül görevdeki istisna 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'.
TaskContinuationOptions.OnlyOnFaulted seçeneğiyle çalıştırıldığından, devamlılık yalnızca önceki aşamada bir özel durum oluştuğunda yürütülür. Bu nedenle, öncül'ün Exception özelliğinin nullolmadığını varsayabilir. Öncülde bir özel durum oluşturulsa da oluşturulmasa da devam işlemi yürütülüyorsa, özel durumu işlemeye çalışmadan önce öncülün Exception özelliğinin null olmadığını denetlemesi gerekir. Aşağıdaki kod parçası bunu göstermektedir:
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.
- Devamlılık, TaskContinuationOptions.AttachedToParent seçeneği kullanılarak oluşturulmuş bağlı bir alt görevse, bu görevin istisnaları, diğer bağlı alt görevlerde olduğu gibi üst görevi tarafından çağıran iş parçacığına geri iletilir. Daha fazla bilgi için bkz. Ekli ve Ayrılmış Alt Görevler.