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.
.NET Framework 4'den başlayarak .NET, zaman uyumsuz veya uzun süre çalışan zaman uyumlu işlemlerin işbirliğiyle iptali için birleşik bir model kullanır. Bu model, iptal belirteci olarak adlandırılan basit bir nesneyi temel alır. İptal edilebilir işlemleri, örneğin yeni iş parçacıkları veya görevler oluşturarak çağıran nesne, belirteci her bir işleme aktarır. Bireysel işlemler, belirtecin kopyalarını diğer işlemlere de aktarabilir. Daha sonra belirteci oluşturan nesne, işlemlerin yaptıklarını durdurmasını istemek için bunu kullanabilir. İptal isteğini yalnızca istekte bulunan nesne verebilir ve her dinleyici isteği fark edip uygun ve zamanında yanıtlamaktan sorumludur.
İşbirlikçi iptal modelini uygulamaya yönelik genel düzen şu şekildedir:
Tek tek iptal belirteçlerini yöneten ve iptal bildirimi gönderen bir CancellationTokenSource nesne örneği oluşturma.
CancellationTokenSource.Token özelliği tarafından döndürülen belirteci, iptali dinleyen her göreve veya iş parçacığına iletin.
İptale yanıt vermek için her görev veya iş parçacığı için bir mekanizma sağlayın.
İptal bildirimini sağlamak için CancellationTokenSource.Cancel yöntemini çağırın.
Önemli
CancellationTokenSource sınıfı, IDisposable arabirimini uygular. İptal belirteci kaynağını kullanmayı bitirdiğinizde içerdiği yönetilmeyen kaynakları serbest bırakmak için CancellationTokenSource.Dispose yöntemini çağırdığınızdan emin olmalısınız.
Aşağıdaki çizimde belirteç kaynağı ile belirtecinin tüm kopyaları arasındaki ilişki gösterilmektedir.
İşbirliğine dayalı iptal modeli, iptale duyarlı uygulamalar ve kitaplıklar oluşturmayı kolaylaştırır ve aşağıdaki özellikleri destekler:
İptal, iş birliğine dayalıdır ve dinleyiciye dayatılmaz. Dinleyici, bir iptal isteğine yanıt olarak nasıl zarif bir şekilde sonlandıracağını belirler.
İstekte bulunmak, dinlemeden farklıdır. İptal edilebilir işlemi çağıran bir nesne, iptal işleminin ne zaman (varsa) istendiğini denetleyebilir.
İstekte bulunan nesne, yalnızca bir yöntem çağrısı kullanarak iptal isteğini belirtecin tüm kopyalarına gönderir.
Dinleyici, birden çok belirteci tek bir bağlı belirteçte birleştirerek aynı anda dinleyebilir.
Kullanıcı kodu kitaplık kodundan gelen iptal isteklerini fark edip yanıtlayabilir ve kitaplık kodu da kullanıcı kodundan gelen iptal isteklerini fark edip yanıtlayabilir.
Dinleyicilere, yoklama yaparak, geri çağırma kaydı yaptırarak veya bekleme tanıtıcıları üzerinde bekleyerek iptal istekleri bildirilebilir.
İptal Türleri
İptal çerçevesi, aşağıdaki tabloda listelenen bir dizi ilgili tür olarak uygulanır.
| Tür adı | Açıklama |
|---|---|
| CancellationTokenSource | bir iptal belirteci oluşturan ve ayrıca bu belirtecin tüm kopyaları için iptal isteği veren nesne. |
| CancellationToken | Genellikle bir yöntem parametresi olarak hafif ağırlıklı değer türü bir veya daha fazla dinleyiciye aktarılır. Belirtecin IsCancellationRequested özelliğinin değerini yoklama, geri çağırma veya bekleme işleyicisi kullanarak izler dinleyiciler. |
| OperationCanceledException | Bu özel durumun oluşturucu aşırı yüklemeleri, CancellationToken'yi parametre olarak kabul eder. Dinleyiciler isteğe bağlı olarak bu özel durumu oluşturarak iptal kaynağını doğrulayabilir ve iptal isteğine yanıt verdiğini başkalarına bildirebilir. |
İptal modeli çeşitli türlerde .NET ile tümleştirilmiştir. En önemlileri , System.Threading.Tasks.ParallelSystem.Threading.Tasks.Task ve System.Threading.Tasks.Task<TResult>'lerdirSystem.Linq.ParallelEnumerable. Bu işbirliğine dayalı iptal modelini tüm yeni kitaplık ve uygulama kodu için kullanmanızı öneririz.
Kod Örneği
Aşağıdaki örnekte, istekte bulunan nesne bir CancellationTokenSource nesnesi oluşturur ve ardından Token özelliğini iptal edilebilir işleme geçirir. İsteği alan işlem, yoklama yaparak belirteç IsCancellationRequested özelliğinin değerini izler. Değer true olduğunda, dinleyici uygun şekilde sonlandırabilir. Bu örnekte, metot yalnızca sona erer, bu da birçok durumda gereken tek şeydir.
Uyarı
Örnek, QueueUserWorkItem işbirliğine dayalı iptal çerçevesinin eski API'lerle uyumlu olduğunu göstermek için yöntemini kullanır. Tercih edilen System.Threading.Tasks.Task türünü kullanan bir örnek için bkz: Görev ve Alt Görevlerini İptal Etme Nasıl Yapılır.
using System;
using System.Threading;
public class Example
{
public static void Main()
{
// Create the token source.
CancellationTokenSource cts = new CancellationTokenSource();
// Pass the token to the cancelable operation.
ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
Thread.Sleep(2500);
// Request cancellation.
cts.Cancel();
Console.WriteLine("Cancellation set in token source...");
Thread.Sleep(2500);
// Cancellation should have happened, so call Dispose.
cts.Dispose();
}
// Thread 2: The listener
static void DoSomeWork(object? obj)
{
if (obj is null)
return;
CancellationToken token = (CancellationToken)obj;
for (int i = 0; i < 100000; i++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("In iteration {0}, cancellation has been requested...",
i + 1);
// Perform cleanup if necessary.
//...
// Terminate the operation.
break;
}
// Simulate some work.
Thread.SpinWait(500000);
}
}
}
// The example displays output like the following:
// Cancellation set in token source...
// In iteration 1430, cancellation has been requested...
Imports System.Threading
Module Example1
Public Sub Main1()
' Create the token source.
Dim cts As New CancellationTokenSource()
' Pass the token to the cancelable operation.
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoSomeWork), cts.Token)
Thread.Sleep(2500)
' Request cancellation by setting a flag on the token.
cts.Cancel()
Console.WriteLine("Cancellation set in token source...")
Thread.Sleep(2500)
' Cancellation should have happened, so call Dispose.
cts.Dispose()
End Sub
' Thread 2: The listener
Sub DoSomeWork(ByVal obj As Object)
Dim token As CancellationToken = CType(obj, CancellationToken)
For i As Integer = 0 To 1000000
If token.IsCancellationRequested Then
Console.WriteLine("In iteration {0}, cancellation has been requested...",
i + 1)
' Perform cleanup if necessary.
'...
' Terminate the operation.
Exit For
End If
' Simulate some work.
Thread.SpinWait(500000)
Next
End Sub
End Module
' The example displays output like the following:
' Cancellation set in token source...
' In iteration 1430, cancellation has been requested...
İşlem İptali ile Nesne İptali Karşılaştırması
İşbirliğine dayalı iptal çerçevesinde iptal, nesneleri değil işlemleri ifade eder. İptal isteği, gerekli temizleme gerçekleştirildikten sonra işlemin en kısa sürede durdurulması gerektiği anlamına gelir. Bir iptal belirteci bir "iptal edilebilir işleme" başvurmalıdır, ancak bu işlem programınızda uygulanabilir. Belirteç özelliği IsCancellationRequested olarak ayarlandıktan sonra true olarak geri alınamaz. Bu nedenle iptal belirteçleri iptal edildikten sonra yeniden kullanılamaz.
Bir nesne iptal mekanizmasına ihtiyacınız varsa, aşağıdaki örnekte gösterildiği gibi yöntemini çağırarak CancellationToken.Register bunu işlem iptal mekanizmasına dayandırabilirsiniz.
using System;
using System.Threading;
class CancelableObject
{
public string id;
public CancelableObject(string id)
{
this.id = id;
}
public void Cancel()
{
Console.WriteLine($"Object {id} Cancel callback");
// Perform object cancellation here.
}
}
public class Example1
{
public static void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// User defined Class with its own method for cancellation
var obj1 = new CancelableObject("1");
var obj2 = new CancelableObject("2");
var obj3 = new CancelableObject("3");
// Register the object's cancel method with the token's
// cancellation request.
token.Register(() => obj1.Cancel());
token.Register(() => obj2.Cancel());
token.Register(() => obj3.Cancel());
// Request cancellation on the token.
cts.Cancel();
// Call Dispose when we're done with the CancellationTokenSource.
cts.Dispose();
}
}
// The example displays the following output:
// Object 3 Cancel callback
// Object 2 Cancel callback
// Object 1 Cancel callback
Imports System.Threading
Class CancelableObject
Public id As String
Public Sub New(id As String)
Me.id = id
End Sub
Public Sub Cancel()
Console.WriteLine("Object {0} Cancel callback", id)
' Perform object cancellation here.
End Sub
End Class
Module ExampleOb1
Public Sub MainOb1()
Dim cts As New CancellationTokenSource()
Dim token As CancellationToken = cts.Token
' User defined Class with its own method for cancellation
Dim obj1 As New CancelableObject("1")
Dim obj2 As New CancelableObject("2")
Dim obj3 As New CancelableObject("3")
' Register the object's cancel method with the token's
' cancellation request.
token.Register(Sub() obj1.Cancel())
token.Register(Sub() obj2.Cancel())
token.Register(Sub() obj3.Cancel())
' Request cancellation on the token.
cts.Cancel()
' Call Dispose when we're done with the CancellationTokenSource.
cts.Dispose()
End Sub
End Module
' The example displays output like the following:
' Object 3 Cancel callback
' Object 2 Cancel callback
' Object 1 Cancel callback
Bir nesne birden fazla eşzamanlı iptal edilebilir işlemi destekliyorsa, her ayrı iptal edilebilir işleme giriş olarak ayrı bir belirteç geçirin. Bu şekilde, bir işlem diğerlerini etkilemeden iptal edilebilir.
İptal İsteklerini Dinleme ve Yanıtlama
Kullanıcı temsilcisinde, iptal edilebilir bir işlemin uygulayıcısı, iptal isteğine yanıt olarak işlemin nasıl sonlandırileceğini belirler. Çoğu durumda, kullanıcı temsilcisi gerekli temizleme işlemlerini gerçekleştirebilir ve hemen geri dönebilir.
Ancak, daha karmaşık durumlarda, kullanıcı temsilcisinin iptalin gerçekleştiğini kitaplık koduna bildirmesi gerekebilir. Böyle durumlarda, işlemi sonlandırmanın doğru yolu temsilcinin ThrowIfCancellationRequested yöntemini çağırmasıdır, bu da bir OperationCanceledException fırlatılmasına neden olur. Kitaplık kodu, kullanıcı temsilcisi iş parçacığında bu özel durumu yakalayabilir ve özel durumun işbirliğine dayalı iptali mi yoksa başka bir istisna durumunu mu gösterdiğini belirlemek için özel durumun belirtecini inceleyebilir.
Task sınıfı bu şekilde işlerOperationCanceledException. Daha fazla bilgi için bkz. Görev İptali.
Yoklama ile Dinleme
Uzun süre çalışan döngü veya özyinelemeli hesaplamalar için, CancellationToken.IsCancellationRequested özelliğinin değerini düzenli aralıklarla kontrol ederek iptal isteğini dinleyebilirsiniz. Değeri ise true, yöntemin olabildiğince hızlı bir şekilde temizlenmesi ve sonlandırılması gerekir. Yoklamanın en uygun sıklığı, uygulama türüne bağlıdır. Belirli bir program için en iyi yoklama sıklığını belirlemek geliştiriciye bağlıdır. Yoklamanın kendisi performansı önemli ölçüde etkilemez. Aşağıdaki örnekte yoklamanın olası bir yolu gösterilmektedir.
static void NestedLoops(Rectangle rect, CancellationToken token)
{
for (int col = 0; col < rect.columns && !token.IsCancellationRequested; col++) {
// Assume that we know that the inner loop is very fast.
// Therefore, polling once per column in the outer loop condition
// is sufficient.
for (int row = 0; row < rect.rows; row++) {
// Simulating work.
Thread.SpinWait(5_000);
Console.Write("{0},{1} ", col, row);
}
}
if (token.IsCancellationRequested) {
// Cleanup or undo here if necessary...
Console.WriteLine("\r\nOperation canceled");
Console.WriteLine("Press any key to exit.");
// If using Task:
// token.ThrowIfCancellationRequested();
}
}
Shared Sub NestedLoops(ByVal rect As Rectangle, ByVal token As CancellationToken)
Dim col As Integer
For col = 0 To rect.columns - 1
' Assume that we know that the inner loop is very fast.
' Therefore, polling once per column in the outer loop condition
' is sufficient.
For row As Integer = 0 To rect.rows - 1
' Simulating work.
Thread.SpinWait(5000)
Console.Write("0',1' ", col, row)
Next
Next
If token.IsCancellationRequested = True Then
' Cleanup or undo here if necessary...
Console.WriteLine(vbCrLf + "Operation canceled")
Console.WriteLine("Press any key to exit.")
' If using Task:
' token.ThrowIfCancellationRequested()
End If
End Sub
Daha eksiksiz bir örnek için bkz . Nasıl yapılır: Yoklama ile İptal İsteklerini Dinleme.
Geri Çağırma Kaydederek Dinleme
Bazı işlemler, iptal belirtecinin değerini zamanında denetleyemeyecek şekilde bloklanabilir. Bu durumlarda, bir iptal isteği alındığında yöntemin engelini kaldıran bir geri çağırma yöntemi kaydedebilirsiniz.
Bu amaç için özel olarak kullanılan bir Register nesneyi döndüren CancellationTokenRegistration yöntemidir. Aşağıdaki örnek, Register yöntemini kullanarak zaman uyumsuz bir web isteğinin nasıl iptal edileceğini göstermektedir.
using System;
using System.Net.Http;
using System.Threading;
class Example4
{
static void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
StartWebRequest(cts.Token);
// Cancellation will cause the web
// request to be cancelled.
cts.Cancel();
}
static void StartWebRequest(CancellationToken token)
{
var client = new HttpClient();
token.Register(() =>
{
client.CancelPendingRequests();
Console.WriteLine("Request cancelled!");
});
Console.WriteLine("Starting request.");
client.GetStringAsync(new Uri("http://www.contoso.com"));
}
}
Imports System.Net
Imports System.Net.Http
Imports System.Threading
Class Example4
Private Shared Sub Main4()
Dim cts As New CancellationTokenSource()
StartWebRequest(cts.Token)
' cancellation will cause the web
' request to be cancelled
cts.Cancel()
End Sub
Private Shared Sub StartWebRequest(token As CancellationToken)
Dim client As New HttpClient()
token.Register(Sub()
client.CancelPendingRequests()
Console.WriteLine("Request cancelled!")
End Sub)
Console.WriteLine("Starting request.")
client.GetStringAsync(New Uri("http://www.contoso.com"))
End Sub
End Class
CancellationTokenRegistration nesnesi iş parçacığı eşitlemesini yönetir ve geri çağırmanın belirli bir noktada yürütülmesini durdurmasını sağlar.
Sistemin yanıt verme hızını sağlamak ve kilitlenmeleri önlemek için geri çağırmaları kaydederken aşağıdaki yönergelere uyulmalıdır:
Geri çağırma yöntemi hızlı olmalıdır çünkü zaman uyumlu olarak çağrılır ve bu nedenle Cancel çağrısı, geri çağırma tamamlanana kadar geri dönmez.
Geri çağırma çalışırken Dispose çağırırsanız ve geri çağırmanın beklediği bir kilidi tutuyorsanız, programınız kilitlenebilir.
Disposegeri döndükten sonra, geri çağırma için gereken tüm kaynakları serbest bırakabilirsiniz.Geri çağırma işlevleri, bir geri çağırma içinde manuel iş parçacığı veya SynchronizationContext kullanımı gerçekleştirmemelidir. Bir geri çağırmanın belirli bir iş parçacığında çalışması gerekiyorsa, hedef syncContext öğesinin System.Threading.CancellationTokenRegistration etkin SynchronizationContext.Currentolduğunu belirtmenize olanak tanıyan oluşturucuyu kullanın. Geri çağırmada el ile iş parçacığı oluşturmak kilitlenmeye neden olabilir.
Daha eksiksiz bir örnek için bkz . Nasıl yapılır: İptal İstekleri için Geri Çağırmaları Kaydetme.
Bekleme Tutamacı Kullanarak Dinleme
İptal edilebilir bir işlem System.Threading.ManualResetEvent veya System.Threading.Semaphore gibi bir eşitleme temel öğesini beklerken engelleyebildiğinde, hem olay hem de iptal isteğinde işlemin beklemesini sağlamak için CancellationToken.WaitHandle özelliğini kullanabilirsiniz. İptal belirtecinin bekleme işleyicisi, bir iptal isteğine yanıt olarak sinyal verilir ve yöntem, sinyali verenin iptal belirteci olup olmadığını belirlemek için WaitAny yönteminin dönüş değerini kullanabilir. İşlem daha sonra yalnızca çıkış yapabilir veya uygun şekilde bir OperationCanceledException atabilir.
// Wait on the event if it is not signaled.
int eventThatSignaledIndex =
WaitHandle.WaitAny(new WaitHandle[] { mre, token.WaitHandle },
new TimeSpan(0, 0, 20));
' Wait on the event if it is not signaled.
Dim waitHandles() As WaitHandle = {mre, token.WaitHandle}
Dim eventThatSignaledIndex =
WaitHandle.WaitAny(waitHandles, _
New TimeSpan(0, 0, 20))
System.Threading.ManualResetEventSlim ve System.Threading.SemaphoreSlim her ikisi de kendi yöntemlerinde iptal çerçevesini Wait destekler.
CancellationToken parametresi yönteme geçirilebilir ve iptal istendiğinde olay uyanır ve bir OperationCanceledException fırlatır.
try
{
// mres is a ManualResetEventSlim
mres.Wait(token);
}
catch (OperationCanceledException)
{
// Throw immediately to be responsive. The
// alternative is to do one more item of work,
// and throw on next iteration, because
// IsCancellationRequested will be true.
Console.WriteLine("The wait operation was canceled.");
throw;
}
Console.Write("Working...");
// Simulating work.
Thread.SpinWait(500000);
Try
' mres is a ManualResetEventSlim
mres.Wait(token)
Catch e As OperationCanceledException
' Throw immediately to be responsive. The
' alternative is to do one more item of work,
' and throw on next iteration, because
' IsCancellationRequested will be true.
Console.WriteLine("Canceled while waiting.")
Throw
End Try
' Simulating work.
Console.Write("Working...")
Thread.SpinWait(500000)
Daha eksiksiz bir örnek için bkz Nasıl Yapılır: Bekleme Tutamaçlarına Sahip İptal İsteklerini Dinleme.
Birden Çok Belirteci Aynı Anda Dinleme
Bazı durumlarda dinleyicinin aynı anda birden çok iptal belirtecini dinlemesi gerekebilir. Örneğin, iptal edilebilir bir işlemin, bir yöntem parametresi olarak dışarıdan geçirilen bir belirtecine ek olarak, dahili bir iptal belirtecini izlemesi gerekebilir. Bunu başarmak için, aşağıdaki örnekte gösterildiği gibi iki veya daha fazla belirteci tek bir belirteçte birleştirebilen bağlı bir belirteç kaynağı oluşturun.
public void DoWork(CancellationToken externalToken)
{
// Create a new token that combines the internal and external tokens.
this.internalToken = internalTokenSource.Token;
this.externalToken = externalToken;
using (CancellationTokenSource linkedCts =
CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken))
{
try
{
DoWorkInternal(linkedCts.Token);
}
catch (OperationCanceledException)
{
if (internalToken.IsCancellationRequested)
{
Console.WriteLine("Operation timed out.");
}
else if (externalToken.IsCancellationRequested)
{
Console.WriteLine("Cancelling per user request.");
externalToken.ThrowIfCancellationRequested();
}
}
}
}
Public Sub DoWork(ByVal externalToken As CancellationToken)
' Create a new token that combines the internal and external tokens.
Dim internalToken As CancellationToken = internalTokenSource.Token
Dim linkedCts As CancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken)
Using (linkedCts)
Try
DoWorkInternal(linkedCts.Token)
Catch e As OperationCanceledException
If e.CancellationToken = internalToken Then
Console.WriteLine("Operation timed out.")
ElseIf e.CancellationToken = externalToken Then
Console.WriteLine("Canceled by external token.")
externalToken.ThrowIfCancellationRequested()
End If
End Try
End Using
End Sub
İşiniz bittikten sonra Dispose'yi bağlı belirteç kaynağında çağırmanız gerektiğine dikkat edin. Daha eksiksiz bir örnek için bkz . Nasıl yapılır: Birden Çok İptal İsteğini Dinleme.
Kitaplık Kodu ile Kullanıcı Kodu Arasında İşbirliği
Birleşik iptal çerçevesi, kitaplık kodunun kullanıcı kodunu ve kullanıcı kodunun da kitaplık kodunu işbirliği içinde iptal etmesini mümkün kılar. Sorunsuz işbirliği, aşağıdaki yönergeleri izleyerek her iki tarafa da bağlıdır:
Kitaplık kodu iptal edilebilir işlemler sağlıyorsa, kullanıcı kodunun iptal isteğinde bulunabilmesi için dış iptal belirtecini kabul eden genel yöntemler de sağlaması gerekir.
Kitaplık kodu kullanıcı koduna çağrıda bulunursa, kitaplık kodu OperationCanceledException(externalToken) ifadesini işbirliğine dayalı iptal olarak yorumlamalı ve mutlaka bir hata özel durumu olarak değerlendirmemelidir.
Kullanıcı temsilcileri, kitaplık kodundan gelen iptal isteklerine zamanında yanıt vermeyi denemelidir.
System.Threading.Tasks.Task ve System.Linq.ParallelEnumerable bu yönergeleri izleyen sınıflara örnektir. Daha fazla bilgi için bkz . Görev İptali ve Nasıl yapılır: PLINQ Sorgusunu İptal Etme.