2015-12-18 17 views
5

Aşağıdaki kod, iptal edilmekte olan bir görev oluşturur. await İfadesi (durum 1), Wait() (durum 2) System.Threading.Tasks.TaskCanceledException (System.AggregateException içinde sarılmış) atarken System.OperationCanceledException'u atar.OperationCanceledException VS GörevKanceledException Görev iptal edildiğinde

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

public class Program 
{ 
    public static void Main() 
    { 
     Program.MainAsync().Wait(); 
    } 

    private static async Task MainAsync() 
    { 
     using(var cancellationTokenSource = new CancellationTokenSource()) 
     { 
      var token = cancellationTokenSource.Token; 
      const int cancelationCheckTimeout = 100; 

      var task = Task.Run(
       async() => 
       { 
        for (var i = 0; i < 100; i++) 
        { 
         token.ThrowIfCancellationRequested(); 
         Console.Write("."); 
         await Task.Delay(cancelationCheckTimeout); 
        } 
       }, 
       cancellationTokenSource.Token 
      ); 

      var cancelationDelay = 10 * cancelationCheckTimeout; 
      cancellationTokenSource.CancelAfter(cancelationDelay); 

      try 
      { 
       await task; // (1) 
       //task.Wait(); // (2) 
      } 
      catch(Exception ex) 
      { 
       Console.WriteLine(ex.ToString()); 
       Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}"); 
       Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}"); 
       Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}"); 
      } 
     } 
    } 
} 

Durum 1 çıkışı:

..........System.OperationCanceledException: The operation was canceled. 
    at System.Threading.CancellationToken.ThrowIfCancellationRequested() 
    at Program.<>c__DisplayClass1_0.<<MainAsync>b__0>d.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at Program.<MainAsync>d__1.MoveNext() 
Task.IsCanceled: True 
Task.IsFaulted: False 
Task.Exception: null 

Durum 2 çıkışı:

..........System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled. 
    --- End of inner exception stack trace --- 
    at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) 
    at System.Threading.Tasks.Task.Wait() 
    at Program.<MainAsync>d__1.MoveNext() 
---> (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.<--- 

Task.IsCanceled: True 
Task.IsFaulted: False 
Task.Exception: null 

Neden System.AggregateException 2 durumda bir iç durum olarak System.OperationCanceledException içermiyor?

Ben ThrowIfCancellationRequested()OperationCanceledException atar biliyoruz ve biz Task iptal (arızalı değil) durumuna alır her iki durumda da görebiliriz.

.NET API bir yöntem iptal her iki durumda da tutarlı davranış üretir, çünkü bu Kafamı kurcalayan - iptal edilen görev sadece TaskCanceledException atar:

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

public class Program 
{ 
    public static void Main() 
    { 
     Program.MainAsync().Wait(); 
    } 

    private static async Task MainAsync() 
    { 
     using(var cancellationTokenSource = new CancellationTokenSource()) 
     { 
      var token = cancellationTokenSource.Token; 

      var task = Task.Delay(1000, token); 
      cancellationTokenSource.CancelAfter(100); 

      try 
      { 
       await task; // (1) 
       //task.Wait(); // (2) 
      } 
      catch(Exception ex) 
      { 
       Console.WriteLine(ex.ToString()); 
       Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}"); 
       Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}"); 
       Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}"); 
      } 
     } 
    } 
} 

Durum 1 çıkış:

System.Threading.Tasks.TaskCanceledException: A task was canceled. 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at Program.<MainAsync>d__1.MoveNext() 
Task.IsCanceled: True 
Task.IsFaulted: False 
Task.Exception: null 

Durum 2 çıkışı:

System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled. 
    --- End of inner exception stack trace --- 
    at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) 
    at System.Threading.Tasks.Task.Wait() 
    at Program.<MainAsync>d__1.MoveNext() 
---> (Inner Exception #0) System.Threading.Tasks.TaskCanceledException: A task was canceled.<--- 

Task.IsCanceled: True 
Task.IsFaulted: False 
Task.Exception: null 
+0

Önemli mi? 'TaskCanceledException' türetilmiş bir“ OperationCanceledException ”sınıfıdır, bu yüzden eğer yakaladıysanız (OperationCanceledException e), her iki istisna tipini de yakalarsınız. Kaydettiğiniz tek bilgi parçası 'TaskCanceledException.Task' özelliği. –

+0

Katılıyorum ama daha önce “OperationCanceledException” var iken “TaskCanceledException” ın tanıtımı/kullanımı nedenlerini merak ettim. –

+1

Bunu tanıtmanın nedeni, bazı durumlarda, Görev'i içeren daha türetilmiş formları atabileceğiniz durumlarda iptal edilen Görev bağlamını yakalayabilmenizdir.'ThrowIfCancellationRequested' genel olarak yazılmıştır, bir Görev'in içinde olduğunu bilmez, bu nedenle Görev özelliği olmadan daha genel bir istisna ortaya çıkarır. –

cevap

5

Buradaki fark,kullanılarak gelir.. Bu yöntem, iptal işlemini kontrol eder ve eğer istenirse OperationCanceledException'u TaskCanceledException (CancellationToken olarak anlaşılabilir) TPL'ye özel değildir. Sen reference source bakmak ve onu bu yöntemi çağırır görebilirsiniz:

private void ThrowOperationCanceledException() 
{ 
    throw new OperationCanceledException(Environment.GetResourceString("OperationCanceled"), this); 
} 

"Düzenli" iptal olsa gerçekten TaskCanceledException üretecektir.

cancellationTokenSource.Cancel(); 
var task = Task.Run(() => { }, cancellationTokenSource.Token); 
try 
{ 
    await task; 
} 
catch (Exception ex) 
{ 
    Console.WriteLine(ex.ToString()); 
    Console.WriteLine($"Task.IsCanceled: {task.IsCanceled}"); 
    Console.WriteLine($"Task.IsFaulted: {task.IsFaulted}"); 
    Console.WriteLine($"Task.Exception: {((task.Exception == null) ? "null" : task.Exception.ToString())}"); 
} 

Çıktı:

System.Threading.Tasks.TaskCanceledException: A task was canceled. 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.GetResult() 
    at Sandbox.Program.<MainAsync>d__1.MoveNext() 
Task.IsCanceled: True 
Task.IsFaulted: False 
Task.Exception: null 

Geleneksel Net yöntemler genellikle zaman uyumsuz API için CancellationToken.ThrowIfCancellationRequested kullanmayın Sen belirteci önce görev yayınlanmaya başlaması için bir şans vardı iptal ederek görebilirsiniz İşi başka bir iş parçacığına yüklerken uygundur. Bu yöntem, doğal olarak eşzamansız işlemler içindir, bu nedenle, iptal işlemi CancellationToken.Register (veya dahili InternalRegisterWithoutEC) kullanılarak izlenir.

+0

.NET yöntemlerini (örn. Task.Delay() 'veya' FileStream.WriteAsync() ') iptal ettiklerini gözlemledim ve iptal edildiklerinde hepsinin 'TaskCanceledException' komutunu attıklarını fark ettim (TPL'ye söylediğiniz gibi CancelPayToken aracılığıyla) . Bu, Görev'in kendisini iptal etmesinin (geri çağrılan dediklerine değil) TaskCanceledException'ı yukarıdaki örneğinizde olduğu gibi atması mantıklıdır. Ama neden FileStream.WriteAsync() '' OperationCanceledException 'atmıyor? 'Başka bir iş parçacığına' boşaltma işini 'genişletebilir misiniz? –

+1

@BojanKomazec 'ThrowIfCancellationRequeded', OperationCanceledException'yi atar. Bu yöntemi yalnızca kod çalıştırdığınızda periyodik olarak iptal olup olmadığını kontrol etmeniz gerekir. Yerleşik uyumsuz API, bunu senkronize olduğu gibi yapmaz. Bir döngüde bir şey yapan bir iş parçacığı tutmuyor. Yani 'ThrowIfCancellationRequested' olarak adlandırılan kod yok. Örneğin, – i3arnon

+1

@BojanKomazec 'Task.Delay', bir söz görevi oluşturur, zaman aşımı sona erdiğinde görevi tamamlayacak bir zamanlayıcıyı başlatır ve 'İptalleToken.Register' ile belirtecin üzerindeki görevi iptal etmek için bir temsilci kaydeder. Daha sonra görevi döndürür ve budur. ThrowIfCancellationRequested 'diye bir yer yok. – i3arnon

İlgili konular