Yönetilen İş Parçacıklarında İptal

.NET Framework 4 ' te başlayarak, .net, zaman uyumsuz veya uzun süreli zaman uyumlu işlemler için birlikte çalışan bir işleme için birleştirilmiş bir model kullanır. Bu model, iptal belirteci adlı hafif bir nesneyi temel alır. Bir veya daha fazla iptal edilebilen işlemi çağıran nesne (örneğin, yeni iş parçacıkları veya görevler oluşturarak) belirteci her bir işleme geçirir. Tek tek işlemler, belirtecin kopyalarını diğer işlemlere geçirebilir. Daha sonra, belirteci oluşturan nesne, işlemin ne yaptığını durdurmasını istemek için bunu kullanabilir. Yalnızca istenen nesne iptal isteğini verebilir ve her dinleyici, isteği yaşıyorsanız ve uygun ve zamanında yanıt verecek şekilde sorumludur.

Birlikte çalışırken iptal modelinin uygulanması için genel bir örüntü şunlardır:

  • CancellationTokenSourceAyrı ayrı iptal belirteçlerine yönelik iptali bildirimini yöneten ve gönderen bir nesne örneği oluşturur.

  • Özelliği tarafından döndürülen belirteci, CancellationTokenSource.Token iptal için dinleyen her bir göreve veya iş parçacığına geçirin.

  • Her görev veya iş parçacığının iptaline yanıt vermesi için bir mekanizma sağlar.

  • CancellationTokenSource.Cancelİptal bildirimi sağlamak için yöntemini çağırın.

Önemli

CancellationTokenSource sınıfı, IDisposable arabirimini uygular. CancellationTokenSource.DisposeTuttuğu yönetilmeyen kaynakları serbest bırakmak için iptal belirteci kaynağını kullanmayı bitirdiğinizde yöntemini çağırmanız gerekir.

Aşağıdaki çizimde, belirteç kaynağı ve belirtecinin tüm kopyalarından oluşan ilişki gösterilmektedir.

CancellationTokenSource ve iptal belirteçleri

Birlikte çalışırken iptal modeli, iptal kullanan uygulamalar ve kitaplıklar oluşturmayı kolaylaştırır ve aşağıdaki özellikleri destekler:

  • İptal etme birlikte çalışır ve dinleyicide zorlanmaz. Dinleyici, bir iptal isteğine yanıt olarak düzgün bir şekilde nasıl sonlandırıldığını belirler.

  • İstek, dinlemeye göre farklıdır. İptal edilebilen bir işlemi çağıran bir nesne, (varsa) iptalinin ne zaman istenmediğini denetleyebilir.

  • İstekte bulunan nesne, yalnızca bir yöntem çağrısı kullanarak, iptal isteğini belirtecin tüm kopyalarına yayınlar.

  • Bir dinleyici, tek bir bağlı belirtece katılarak birden fazla belirteci aynı anda dinleyebilir.

  • Kullanıcı kodu kitaplık kodundan gelen iptal isteklerini fark edebilir ve yanıtlayabilir ve kitaplık kodu kullanıcı kodundan gelen iptal isteklerini görebilir ve bunlara yanıt verebilir.

  • Dinleyiciler yoklamaya, geri çağırma kaydına veya bekleme tanıtıcılarını beklemeye göre iptal istekleri hakkında bildirim alabilir.

İptal türleri

İptal çerçevesi, aşağıdaki tabloda listelenen ilgili türler kümesi olarak uygulanır.

Tür adı Description
CancellationTokenSource İptal belirteci oluşturan ve ayrıca söz konusu belirtecin tüm kopyalarının iptal isteğini veren nesne.
CancellationToken Hafif değer türü, genellikle yöntem parametresi olarak bir veya daha fazla dinleyicilerine geçirildi. Dinleyiciler, IsCancellationRequested belirteç özelliğinin değerini yoklama, geri çağırma veya bekleme tanıtıcısıyla izler.
OperationCanceledException Bu özel durumun oluşturucusunun aşırı yüklemeleri CancellationToken parametre olarak bir parametresini kabul eder. Bu özel durum, İptalin kaynağını doğrulamak ve diğer kullanıcılara bir iptal isteğine yanıt verdiğini bildirmek için isteğe bağlı olarak bu özel durumu oluşturabilir.

İptal modeli, .NET ile çeşitli türlerde tümleştirilir. En önemli olanlar System.Threading.Tasks.Parallel , ve ' System.Threading.Tasks.Task dir System.Threading.Tasks.Task<TResult> System.Linq.ParallelEnumerable . Tüm yeni kitaplık ve uygulama kodu için bu ortak iptal modelini kullanmanızı öneririz.

Kod Örneği

Aşağıdaki örnekte, istenen nesne bir CancellationTokenSource nesne oluşturur ve sonra Token özelliğini iptal edilebilen işleme geçirir. İsteği alan işlem, IsCancellationRequested belirteç özelliğinin değerini yoklayarak izler. Değer ne olursa true olsun, dinleyici uygun şekilde sonlandırılır. Bu örnekte, yöntemi birçok durumda gerekli olan tek çıkar.

Not

Örnek, QueueUserWorkItem birlikte çalışmayan iptal çerçevesinin eski API 'lerle uyumlu olduğunu göstermek için yöntemini kullanır. Tercih edilen türü kullanan bir örnek için System.Threading.Tasks.Task bkz. nasıl yapılır: bir görevi ve alt öğelerini iptal etme.

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)
   {
      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 Example
    Public Sub Main()
        ' 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 Iptali ve nesne Iptaline karşı

Birlikte çalışma iptal çerçevesinde, iptal etme işlemleri nesneleri değil, işlemler anlamına gelir. İptal isteği, gerekli temizleme gerçekleştirildikten sonra işlemin en kısa sürede durması gerektiği anlamına gelir. Bir iptal belirtecinin "iptal edilebilen işlem" öğesine başvurması gerekir, ancak bu işlem programınızda uygulanabilir. IsCancellationRequestedBelirtecinin özelliği olarak ayarlandıktan sonra true , ' a sıfırlanamaz false . Bu nedenle, iptal belirteçleri iptal edildikten sonra yeniden kullanılamaz.

Bir nesne iptali mekanizmasına ihtiyacınız varsa, CancellationToken.Register Aşağıdaki örnekte gösterildiği gibi yöntemini çağırarak işlem iptali mekanizmasına temel alabilirsiniz.

using System;
using System.Threading;

class CancelableObject
{
   public string id;

   public CancelableObject(string id)
   {
      this.id = id;
   }

   public void Cancel()
   {
      Console.WriteLine("Object {0} Cancel callback", id);
      // Perform object cancellation here.
   }
}

public class Example
{
   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 Example
    Public Sub Main()
        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 edilebilen işlemi destekliyorsa, ayrı bir iptal edilebilen her işlem için ayrı bir belirteç girişi olarak geçirin. Bu şekilde, bir işlem diğerleri etkilenmeden iptal edilebilir.

Iptal Isteklerini dinleme ve yanıtlama

Kullanıcı temsilcisinde, iptal edilebilen bir işlemin uygulayıcısı bir iptal isteğine yanıt olarak işlemin nasıl sonlandırıldığını belirler. Çoğu durumda, kullanıcı temsilcisi gerekli temizleme işlemini gerçekleştirebilir ve hemen geri dönebilir.

Bununla birlikte, daha karmaşık durumlarda, kullanıcı temsilcisinin iptal eden kitaplık kodunu bildirmesi gerekebilir. Bu gibi durumlarda, işlemi sonlandırmak için doğru yol, ThrowIfCancellationRequested bir, oluşturulmasına neden olacak şekilde temsilcinin, yöntemini çağırmasına yöneliktir OperationCanceledException . Kitaplık kodu, kullanıcı temsilcisi iş parçacığında bu özel durumu yakalayabilir ve özel durumun, ortak iptali mi yoksa başka bir özel durum mi olduğunu anlamak için özel durumun belirtecini inceleyebilirsiniz.

TaskSınıfı OperationCanceledException Bu şekilde işler. Daha fazla bilgi için bkz. Görev iptali.

Yoklamayla dinleme

Döngü veya recurse sağlayan uzun süre çalışan hesaplamalar için, özelliğin değerini düzenli aralıklarla yoklayarak bir iptal isteği dinleyebilirsiniz CancellationToken.IsCancellationRequested . Değeri ise true , yöntemin en kısa sürede temizlenmesi ve sonlandırılması gerekir. En iyi yoklama sıklığı, uygulamanın türüne bağlıdır. Bu, belirli bir program için en iyi yoklama sıklığını belirlemede geliştiriciye yöneliktir. Yoklama, performansı önemli ölçüde etkilemez. Aşağıdaki örnekte, yoklamaya yönelik olası bir yol gösterilmektedir.

static void NestedLoops(Rectangle rect, CancellationToken token)
{
   for (int x = 0; x < rect.columns && !token.IsCancellationRequested; x++) {
      for (int y = 0; y < rect.rows; y++) {
         // Simulating work.
         Thread.SpinWait(5000);
         Console.Write("{0},{1} ", x, y);
      }

      // Assume that we know that the inner loop is very fast.
      // Therefore, checking once per row is sufficient.
      if (token.IsCancellationRequested) {
         // Cleanup or undo here if necessary...
         Console.WriteLine("\r\nCancelling after row {0}.", x);
         Console.WriteLine("Press any key to exit.");
         // then...
         break;
         // ...or, if using Task:
         // token.ThrowIfCancellationRequested();
      }
   }
}
Shared Sub NestedLoops(ByVal rect As Rectangle, ByVal token As CancellationToken)
    For x As Integer = 0 To rect.columns
        For y As Integer = 0 To rect.rows
            ' Simulating work.
            Thread.SpinWait(5000)
            Console.Write("0' end block,1' end block ", x, y)
        Next

        ' Assume that we know that the inner loop is very fast.
        ' Therefore, checking once per row is sufficient.
        If token.IsCancellationRequested = True Then
            ' Cleanup or undo here if necessary...
            Console.WriteLine(vbCrLf + "Cancelling after row 0' end block.", x)
            Console.WriteLine("Press any key to exit.")
            ' then...
            Exit For
            ' ...or, if using Task:
            ' token.ThrowIfCancellationRequested()
        End If
    Next
End Sub

Daha kapsamlı bir örnek için bkz. nasıl yapılır: yoklama Ile Iptal Isteklerini dinleme.

Geri çağırma kaydederek dinleme

Bazı işlemler, iptal belirtecinin değerini zamanında denetlenebilmeleri için bu şekilde engellenmiş hale gelebilir. Bu gibi durumlarda, bir iptal isteği alındığında yöntemini engelleyen bir geri çağırma yöntemi kaydedebilirsiniz.

RegisterYöntemi, CancellationTokenRegistration Bu amaçla özel olarak kullanılan bir nesne döndürür. Aşağıdaki örnek, Register zaman uyumsuz bir web isteğini iptal etmek için yönteminin nasıl kullanılacağını gösterir.

using System;
using System.Net;
using System.Threading;

class Example
{
    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)
    {
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += (s, e) => Console.WriteLine("Request completed.");

        // Cancellation on the token will
        // call CancelAsync on the WebClient.
        token.Register(() =>
        {
            wc.CancelAsync();
            Console.WriteLine("Request cancelled!");
        });

        Console.WriteLine("Starting request.");
        wc.DownloadStringAsync(new Uri("http://www.contoso.com"));
    }
}
Imports System.Net
Imports System.Threading

Class Example
    Private Shared Sub Main()
        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 wc As New WebClient()
        wc.DownloadStringCompleted += Function(s, e) Console.WriteLine("Request completed.")

        ' Cancellation on the token will 
        ' call CancelAsync on the WebClient.
        token.Register(Function()
                           wc.CancelAsync()
                           Console.WriteLine("Request cancelled!")

                       End Function)

        Console.WriteLine("Starting request.")
        wc.DownloadStringAsync(New Uri("http://www.contoso.com"))
    End Sub
End Class

CancellationTokenRegistrationNesnesi, iş parçacığı eşitlemesini yönetir ve geri aramanın yürütme işlemini zaman içinde kesin bir noktada durdurmasını sağlar.

Sistem yanıt verdiğini sağlamak ve kilitlenmeleri önlemek için geri çağrılar kaydedilirken aşağıdaki yönergelerin izlenmesi gerekir:

  • Geri arama yöntemi, zaman uyumlu olarak çağrıldığı ve bu nedenle çağrısı Cancel geri çağırma görünene kadar döndürülmediği için hızlı olmalıdır.

  • DisposeGeri çağırma sırasında çağrı yaparsanız ve geri aramanın beklediği bir kilit tutarsanız, programınız kilitlenebilir. DisposeGeri döndüğünde, geri çağırma için gereken tüm kaynakları serbest bırakabilirsiniz.

  • Geri çağrılar hiçbir el ile iş parçacığı veya SynchronizationContext bir geri aramada kullanım gerçekleştirmemelidir. Belirli bir iş parçacığında bir geri çağırma çalışması gerekiyorsa, System.Threading.CancellationTokenRegistration hedef syncContext 'in etkin olduğunu belirtmenize olanak sağlayan oluşturucuyu kullanın SynchronizationContext.Current . Bir geri aramada el ile iş parçacığı gerçekleştirmek kilitlenmeye neden olabilir.

Daha kapsamlı bir örnek için bkz. nasıl yapılır: Iptal istekleri Için geri çağırmaları kaydetme.

Bekleme tutamacı kullanarak dinleme

İptal edilebilen bir işlem, veya gibi bir eşitleme temel aldığı sırada engelleyebilen zaman, System.Threading.ManualResetEvent System.Threading.Semaphore CancellationToken.WaitHandle işlemin hem olay hem de iptal isteğinde beklemesini sağlamak için özelliğini kullanabilirsiniz. İptal belirtecinin bekleme tutamacı bir iptal isteğine yanıt olarak sinyal verecektir ve yöntemi, WaitAny sinyal döndüren iptal belirteci olup olmadığını anlamak için yönteminin dönüş değerini kullanabilir. İşlem daha sonra yalnızca çıkabilir veya uygun şekilde bir oluşturabilir OperationCanceledException .

// 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 destekler Wait . CancellationTokenYöntemine geçirebilirsiniz ve iptal istendiğinde olay uyandırır ve bir oluşturur OperationCanceledException .

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 kapsamlı bir örnek için bkz. nasıl yapılır: bekleme tanıtıcıları Içeren Iptal Isteklerini dinleme.

Aynı anda birden çok belirtece dinleme

Bazı durumlarda, bir dinleyicinin aynı anda birden fazla iptal belirtecini dinlemesi gerekebilir. Örneğin, bir iptal edilebilen işlemin, dışarıdan bir yöntem parametresine bağımsız değişken olarak geçirilen bir belirtece ek olarak iç iptal belirtecini izlemesi gerekebilir. Bunu gerçekleştirmek için, aşağıdaki örnekte gösterildiği gibi, iki veya daha fazla belirteci bir belirtece birleştiren bağlantı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

Disposeİle işiniz bittiğinde bağlantılı belirteç kaynağını çağırmanız gerektiğini unutmayın. Daha kapsamlı bir örnek için bkz. nasıl yapılır: birden çok Iptal Isteğini dinleme.

Kitaplık kodu ve Kullanıcı kodu arasındaki ortak işlem

Birleşik iptal çerçevesi, kitaplık kodunun kullanıcı kodunu iptal etmelerini ve kullanıcı kodunun kitaplık kodunu işbirliğine uygun bir şekilde iptal etmelerini mümkün kılan bir çerçevedir. Sorunsuz işbirliği şu yönergelere göre her tarafa bağlıdır:

  • Kitaplık kodu iptal edilebilir işlemler sağlarsa, kullanıcı kodunun iptal isteğinde olması için dış iptal belirteci kabul eden genel yöntemler de sağlansa da gerekir.

  • Kitaplık kodu kullanıcı koduna çağrılmışsa, kitaplık kodu operationCanceledException(externalToken) işbirliği iptali olarak yorumlamalı ve hata özel durumu olarak yorumlanamaz.

  • Kullanıcı temsilcileri, kitaplık kodundan gelen iptal isteklerini zamanında yanıtlamayı denemeli.

System.Threading.Tasks.Task ve System.Linq.ParallelEnumerable , bu yönergelere uygun sınıf örnekleridir. Daha fazla bilgi için bkz. Görev İptali ve Nasıl İşle: PLINQ Sorgusunu İptal Etme.

Ayrıca bkz.