2014-08-28 238 views
1

我有以下代码,其中Task可以被取消,但我基本上需要等待它完成(以确保完整性),然后将OperationCanceledException扔给调用者。等待取消的任务在继续执行之前完成

public static void TaskCancellationTest() { 
    try { 
     Console.WriteLine("TaskCancellationTest started."); 
     var cts = new CancellationTokenSource(); 
     var t = Task.Run(() => { 
      if (cts.Token.IsCancellationRequested) return; 
      Console.WriteLine("1"); 
      Task.Delay(2000).Wait(); 
      Console.WriteLine("2"); 
     }).ContinueWith(task => { 
      if (cts.Token.IsCancellationRequested) return; 
      Console.WriteLine("3"); 
      Task.Delay(2000).Wait(); 
      Console.WriteLine("4"); 
     }); 
     Task.Run(() => { 
      Task.Delay(1000).Wait(); 
      Console.WriteLine("Cancelling..."); 
      cts.Cancel(); 
     }); 
     t.Wait(); 
     try { 
      cts.Token.ThrowIfCancellationRequested(); 
     } 
     catch (OperationCanceledException) { 
      Console.WriteLine("Gracefully canceled."); 
     } 
     Console.WriteLine("TaskCancellationTest completed."); 
    } 
    catch (Exception ex) { 
     Console.WriteLine("TaskCancellationTest... Failure: " + ex); 
    } 
} 

结果如市场预期,是:

1 
Cancelling... 
2 
Gracefully canceled. 

它的工作原理,但我宁愿到CancellationToken传递给方法,因为我明白这是一个更好的模式。我还希望能够观察方法体内的令牌,并呼叫ThrowIfCancellationRequested()放弃而不必等待下一个ContinueWith()。

我正在玩下面的替代代码,这也适用,但有没有办法让OperationCanceledException而不是AggregateException

如果我通过的CancellationToken到WaitAll()方法,问题是,它会在令牌的消除立即抛出OperationCanceledException,而不是等待的任务t1t2实际完成(他们将继续在后台运行)然后只抛出异常。

public static void TaskCancellationTest2() { 
    try { 
     Console.WriteLine("TaskCancellationTest2 started."); 
     var cts = new CancellationTokenSource(); 
     var t1 = Task.Run(() => { 
      Console.WriteLine("1"); 
      Task.Delay(2000).Wait(); 
      Console.WriteLine("2"); 
     }, cts.Token); 
     var t2 = t1.ContinueWith(task => { 
      Console.WriteLine("3"); 
      Task.Delay(2000).Wait(); 
      cts.Token.ThrowIfCancellationRequested(); 
      Console.WriteLine("4"); 
     }, cts.Token); 
     Task.Run(() => { 
      Task.Delay(1000).Wait(); 
      Console.WriteLine("Cancelling..."); 
      cts.Cancel(); 
     }); 
     try { 
      try { 
       Task.WaitAll(t1, t2); 
      } 
      catch (AggregateException ae) { 
       if (ae.InnerExceptions.Count == 1 && ae.InnerExceptions.Single() is OperationCanceledException) { 
        throw ae.InnerExceptions.Single(); 
       } 
       throw; 
      } 
     } 
     catch (OperationCanceledException) { 
      Console.WriteLine("Gracefully canceled."); 
     } 
     Console.WriteLine("TaskCancellationTest2 completed."); 
    } 
    catch (Exception ex) { 
     Console.WriteLine("TaskCancellationTest2... Failure: " + ex); 
    } 
} 

我准备了一把小提琴here

This question的标题与我的非常相似,但是接受的答案不幸与我的情况无关。

你知道有什么方法可以达到我想要的效果吗?尽量使用CancellationToken

回答

1

我认为如果设置了CancellationToken,TPL就是专门用来完成任务的。部分原因是您看到此行为是因为您致电t.Wait(cts.Token)。即使任务没有运行到完成,如果令牌被设置,需要CancellationToken的超载也将停止等待。

ContinueWith一样,如果您传递CancellationToken任务可以在设置该令牌后立即完成。

将代码更改为不带令牌的t.Wait()ContinueWith,您将获得所需的行为。

public static void TaskCancellationTestNotWorking1() 
    { 
     try 
     { 
      Console.WriteLine("TaskCancellationTestNotWorking started."); 
      var cts = new CancellationTokenSource(); 
      var t = Task.Run(() => 
      { 
       Console.WriteLine("1"); 
       Thread.Sleep(2000); 
       Console.WriteLine("2"); 
      }, cts.Token).ContinueWith(task => 
      { 
       Console.WriteLine("3"); 
       Thread.Sleep(2000); 
       cts.Token.ThrowIfCancellationRequested(); 
       Console.WriteLine("4"); 
      }); 

      Task.Run(() => 
      { 
       Thread.Sleep(1000); 
       Console.WriteLine("Cancelling..."); 
       cts.Cancel(); 
      }, cts.Token); 

      try 
      { 
       t.Wait(); 
      } 
      catch (OperationCanceledException) 
      { 
       Console.WriteLine("IsCanceled " + t.IsCanceled); 
       Console.WriteLine("IsCompleted " + t.IsCompleted);      

       Console.WriteLine("Gracefully canceled."); 
      } 
      catch (AggregateException) 
      { 
       Console.WriteLine("IsCanceled " + t.IsCanceled); 
       Console.WriteLine("IsCompleted " + t.IsCompleted); 

       Console.WriteLine("Gracefully canceled 1."); 
      } 

      Console.WriteLine("TaskCancellationTestNotWorking completed."); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("TaskCancellationTestNotWorking... Failure: " + ex); 
     } 
    } 

您可能会发现这篇文章有用How do I cancel non-cancelable async operations?

+0

如果我不叫't.Wait()','然后Console.WriteLine( “2”);'会后'TaskCancellationTestNotWorking异步执行( )'会返回。这是我需要避免的(如果我正在编写文件而不是写入“2”,则需要确保TaskCancellationTestNotWorking()不会在文件写入操作完成之前返回)。 – 2014-08-28 04:49:27

+0

如果任务处于“已取消”状态,则“IsCompleted”将返回true,尽管它仍可能正在执行代码。 – 2014-08-28 04:51:12

+0

你已经调用了'Wait()'并且它抛出了一个异常,你为什么要再次调用它? – 2014-08-28 06:08:24

相关问题