CancellationToken özelliği nasıl kullanılır?


118

RulyCanceler sınıfı için önceki kodla karşılaştırıldığında , kullanarak kodu çalıştırmak istedim CancellationTokenSource.

İptal Jetonlarında belirtildiği gibi , yani bir istisna atmadan / yakalamadan nasıl kullanırım ? IsCancellationRequestedMülkü kullanabilir miyim ?

Bunu şu şekilde kullanmaya çalıştım:

cancelToken.ThrowIfCancellationRequested();

ve

try
{
  new Thread(() => Work(cancelSource.Token)).Start();
}
catch (OperationCanceledException)
{
  Console.WriteLine("Canceled!");
}

ancak bu, cancelToken.ThrowIfCancellationRequested();yöntemde bir çalışma zamanı hatası verdi Work(CancellationToken cancelToken):

System.OperationCanceledException was unhandled
  Message=The operation was canceled.
  Source=mscorlib
  StackTrace:
       at System.Threading.CancellationToken.ThrowIfCancellationRequested()
       at _7CancellationTokens.Token.Work(CancellationToken cancelToken) in C:\xxx\Token.cs:line 33
       at _7CancellationTokens.Token.<>c__DisplayClass1.<Main>b__0() in C:\xxx\Token.cs:line 22
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

Başarıyla çalıştırdığım kod, yeni iş parçacığında OperationCanceledException'ı yakaladı:

using System;
using System.Threading;
namespace _7CancellationTokens
{
  internal class Token
  {
    private static void Main()
    {
      var cancelSource = new CancellationTokenSource();
      new Thread(() =>
      {
         try
         {
           Work(cancelSource.Token); //).Start();
         }
         catch (OperationCanceledException)
         {
            Console.WriteLine("Canceled!");
         }
         }).Start();

      Thread.Sleep(1000);
      cancelSource.Cancel(); // Safely cancel worker.
      Console.ReadLine();
    }
    private static void Work(CancellationToken cancelToken)
    {
      while (true)
      {
        Console.Write("345");
        cancelToken.ThrowIfCancellationRequested();
      }
    }
  }
}

2
docs.microsoft.com/en-us/dotnet/standard/threading/…CancellationTokenSource zaman uyumsuz yöntemlerle kullanım, yoklama ile uzun çalışma yöntemleri ve bir geri arama kullanmanın oldukça iyi örnekleri vardır .
Ehtesh Choudhury

Bu makaleler, sahip olduğunuz seçenekleri ve özel durumunuza göre belirteci kullanmanız gerektiğini gösterir.
Ognyan Dimitrov

Yanıtlar:


140

Çalışma yönteminizi aşağıdaki şekilde uygulayabilirsiniz:

private static void Work(CancellationToken cancelToken)
{
    while (true)
    {
        if(cancelToken.IsCancellationRequested)
        {
            return;
        }
        Console.Write("345");
    }
}

Bu kadar. İptali her zaman kendi başınıza halletmeniz gerekir - çıkış için uygun zaman geldiğinde yöntemden çıkın (böylece çalışmanız ve verileriniz tutarlı durumda olur)

GÜNCELLEME: Yazmamayı tercih ederim while (!cancelToken.IsCancellationRequested)çünkü genellikle döngü gövdesinde güvenli bir şekilde yürütmeyi durdurabileceğiniz birkaç çıkış noktası vardır ve döngü genellikle çıkmak için bazı mantıksal koşullara sahiptir (koleksiyondaki tüm öğeler üzerinde yineleme vb.). Bu yüzden, farklı niyetleri olduğu için bu koşulları karıştırmamanın daha iyi olduğuna inanıyorum.

Önlemeyle ilgili uyarı notu CancellationToken.ThrowIfCancellationRequested():

Söz konusu Comment tarafından Eamon Nerbonne :

... bu cevabın dediği gibi, ThrowIfCancellationRequestedbir dizi IsCancellationRequestedçıkış kontrolünü incelikle değiştirmek . Ancak bu sadece bir uygulama detayı değildir; bu, gözlemlenebilir davranışı etkiler: görev artık iptal edilmiş durumda değil RanToCompletion. Ve bu sadece açık durum kontrollerini değil, aynı zamanda, daha ince bir şekilde, örneğin kullanılana ContinueWithbağlı olarak görev zincirini de etkileyebilir TaskContinuationOptions. Kaçınmanın ThrowIfCancellationRequestedtehlikeli bir tavsiye olduğunu söyleyebilirim .


1
Teşekkürler! Bu, çevrimiçi metinden uymuyor, oldukça yetkili (kitap "Özetle C # 4.0"?) Alıntı yaptım. Bana "her zaman" hakkında bir referans verebilir misiniz?
Fulproof

1
Bu uygulama ve deneyimden kaynaklanır =). Bunu nereden bildiğimi hatırlayamıyorum. "Her zaman ihtiyacınız var" ı kullandım çünkü işçi iş parçacığını dışarıdan Thread.Abort () kullanarak istisna dışında kesebilirsiniz, ancak bu çok kötü bir uygulama. Bu arada, CancellationToken.ThrowIfCancellationRequested () 'i kullanmak aynı zamanda "iptali kendi başınıza halledmektir", diğer bir yoludur.
Sasha

1
@OleksandrPshenychnyy while (true) yerine while (! CancelToken.IsCancellationRequested) demek istedim. Bu yardımcı oldu! Teşekkürler!
Doug Dawson

1
@ Fullproof Bir çalışma zamanının çalışan kodu iptal etmenin genel bir yolu yoktur çünkü çalışma zamanları bir işlemin nerede kesintiye uğrayabileceğini bilecek kadar akıllı değildir. Bazı durumlarda bir döngüden basitçe çıkmak mümkündür, diğer durumlarda daha karmaşık mantık gereklidir, yani işlemlerin geri alınması gerekir, kaynakların serbest bırakılması gerekir (örn. Dosya tanıtıcıları veya ağ bağlantıları). Bu nedenle, bir kod yazmak zorunda kalmadan bir görevi iptal etmenin sihirli bir yolu yoktur. Düşündüğünüz şey bir süreci öldürmek gibidir, ancak bu bir uygulamanın başına gelebilecek en kötü şeylerden biridir çünkü temizlenemez.
user3285954

1
@kosist Manuel olarak başlattığınız işlemi iptal etmeyi planlamıyorsanız CancellationToken.None kullanabilirsiniz. Elbette, sistem süreci öldürüldüğünde, her şey kesintiye uğrar ve CancellationToken'in bununla hiçbir ilgisi yoktur. Yani evet, CancellationTokenSource'u yalnızca işlemi iptal etmek için gerçekten kullanmanız gerekiyorsa oluşturmalısınız. Kullanmadığınız bir şeyi yaratmanın bir anlamı yok.
Sasha

26

@ BrainSlugs83

Stackoverflow'da yayınlanan her şeye körü körüne güvenmemelisiniz. Jens kodundaki yorum yanlıştır, parametre istisnaların atılıp atılmadığını kontrol etmez.

MSDN, bu parametrenin neyi kontrol ettiği çok açık, okudunuz mu? http://msdn.microsoft.com/en-us/library/dd321703(v=vs.110).aspx

Eğer throwOnFirstExceptiondoğruysa, bir istisna hemen işleniyor kalan geri aramalar ve iptal işlemleri önler İptal çağrısına dışına yaymak. Eğer throwOnFirstExceptionyanlış, bu aşırı bir içine gönderilen istisnaları toplamasıdır AggregateExceptionistisna atan bir geri arama çalıştırılmak üzere diğer kayıtlı geri engellemez şekilde.

Değişken adı da yanlıştır çünkü İptal CancellationTokenSourcebelirtecin kendisine değil çağrılır ve kaynak yönettiği her belirtecin durumunu değiştirir.


Ayrıca, iptal belirtecinin önerilen kullanımı hakkında buradaki belgelere (TAP) bir göz atın: docs.microsoft.com/en-us/dotnet/standard/…
Epstone

1
Bu çok faydalı bir bilgi, ancak sorulan soruya hiç cevap vermiyor.
11nallan11

16

İptal jetonlu bir Görev oluşturabilir, arka plana gittiğinizde bu jetonu iptal edebilirsiniz.

Bunu PCL https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/app-lifecycle'da yapabilirsiniz.

var cancelToken = new CancellationTokenSource();
Task.Factory.StartNew(async () => {
    await Task.Delay(10000);
    // call web API
}, cancelToken.Token);

//this stops the Task:
cancelToken.Cancel(false);

Anther çözümü, Xamarin.Forms'daki kullanıcı Zamanlayıcısıdır, uygulama arka plana gittiğinde zamanlayıcıyı durdurun https://xamarinhelp.com/xamarin-forms-timer/


10

Sen edebilirsiniz kullanmak ThrowIfCancellationRequestedistisna işleme olmadan!

Kullanımının ThrowIfCancellationRequesteda Task(a değil Thread) içinden kullanılması amaçlanmıştır . A içinde kullanıldığında Task, istisnayı kendiniz halletmeniz (ve İşlenmemiş İstisna hatasını almanız) gerekmez. Bu, öğesinden ayrılmakla sonuçlanacak Taskve Task.IsCancelledmülk Doğru olacaktır. İstisna işlemine gerek yoktur.

Belirli bir durumda, değiştirmek Threadbir için Task.

Task t = null;
try
{
    t = Task.Run(() => Work(cancelSource.Token), cancelSource.Token);
}

if (t.IsCancelled)
{
    Console.WriteLine("Canceled!");
}

Neden kullanıyorsun t.Start()ve kullanmıyorsun Task.Run()?
Xander Luciano

1
@XanderLuciano: Bu örnekte belirli bir neden yok ve Task.Run () daha iyi bir seçim olurdu.
Titus

5

Sen geçmek zorunda CancellationTokenolacak periyodik iptal talep olup olmadığını görmek için belirteci izler Task, için.

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;  
Task task = Task.Run(() => {     
  while(!token.IsCancellationRequested) {
      Console.Write("*");         
      Thread.Sleep(1000);
  }
}, token);
Console.WriteLine("Press enter to stop the task"); 
Console.ReadLine(); 
cancellationTokenSource.Cancel(); 

Bu durumda iptal istendiğinde işlem sona erecek ve durumu Taskolacaktır RanToCompletion. Görevinizin iptal edildiğinin kabul edilmesini istiyorsanız, ThrowIfCancellationRequestedbir OperationCanceledExceptionistisna atmak için kullanmalısınız .

Task task = Task.Run(() =>             
{                 
    while (!token.IsCancellationRequested) {
         Console.Write("*");                      
        Thread.Sleep(1000);                 
    }           
    token.ThrowIfCancellationRequested();               
}, token)
.ContinueWith(t =>
 {
      t.Exception?.Handle(e => true);
      Console.WriteLine("You have canceled the task");
 },TaskContinuationOptions.OnlyOnCanceled);  

Console.WriteLine("Press enter to stop the task");                 
Console.ReadLine();                 
cancellationTokenSource.Cancel();                 
task.Wait(); 

Umarım bu daha iyi anlamaya yardımcı olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.