2013-10-10 44 views
2

我有一个Task s的延续链,可能会被取消,比如发生超时。我想确定取消发生时哪个任务正在运行。这里就是我说的:取消后继续链中的哪个任务正在运行?

CancellationTokenSource cts = new CancellationTokenSource(); 

Task timeoutTask = Task.Delay(5000).ContinueWith((t) => cts.Cancel()); 

Task workChain = Task.Factory.StartNew((t) => 
{ 
    Console.WriteLine("Running task " + Task.CurrentId); 
    Thread.Sleep(1000); 
}, -1, cts.Token); 

Task parent = workChain; 
for (int i = 0; i < 10; i++) 
{ 
    parent = parent.ContinueWith((t, o) => 
     { 
      Console.WriteLine("Running task " + Task.CurrentId); 
      Console.WriteLine("Last Task.AsyncState = " + t.AsyncState); 
      Thread.Sleep(1000); 
     }, i, cts.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); 
} 
parent.ContinueWith((t) => 
{ 
    Console.WriteLine("Cancel task " + Task.CurrentId); 
    Console.WriteLine("Last running Task.AsyncState = " + t.AsyncState); 
}, TaskContinuationOptions.OnlyOnCanceled); 

当我运行上面,传递到OnlyOnCanceled一个先行词是不是当事情被取消时正在运行的任务。我知道为什么:OnlyOnCanceled任务父节点由循环中创建的最后一个任务派生,并且当发生超时时,所有未完成的任务都被标记为取消而未启动。

让每个任务检查令牌的状态并在其他地方存储其他地方的工作,但是在下一个任务开始之前,一个任务完成后发生取消的可能性很小。在那种情况下,我没有发现任何关于第一个被取消的任务。当任务开始时,我可以随时存储一些东西,如果取消了,我可以存储其他东西,但是这很快就会感觉到非常糟糕。

+1

所以,你说你想知道取消发生时哪个任务正在运行,但正如你所说的那样,它可能不是。取消可能会在一个结束后和下一个开始之前发生。你想知道成功完成的最后一项任务,或者*没有成功完成的第一项任务吗? – Servy

+2

注意,如果您希望CTS在5000毫秒内取消任务,只需在CTS构造函数中放入5000;比你所做的更简单。 – Servy

+0

为什么使用'ContinueWith()'而不是'await'?你为什么使用'Task'链而不是一个带有循环的'Task'? – svick

回答

0

我发现对我的作品,但仍然不觉得干净,因为它应该:

  • 在主链中的每个任务不走CancellationToken,但是却能得到延续第二(不属于的主链)运行OnlyOnFaulted
  • 这些任务中的每一个都在子任务中工作,然后通过调用.Wait(CancellationToken)来等待子任务。当令牌被取消时,等待调用会抛出一个异常,从而使正在运行的任务失效。这样可以报告错误而无需等待子任务完成。
  • 主链中的每项任务都运行OnlyOnRanToCompletion
  • 运行OnlyOnRanToCompletion的主链末尾的一个任务报告整个链的成功(即无超时)。

当令牌被取消,当前正在运行的任务停止等待与OperationCanceledException其子任务。该任务的分支处理异常(或任何其他异常),通过在前面的AggregateException.InnerExceptions中特别包含OperationCanceledException的存在。由于主链中的任务发生故障,主链中不会有其他任务运行。

CancellationTokenSource cts = new CancellationTokenSource(25000); 

Task workChain = Task.Factory.StartNew((o) => 
{ 
    Console.WriteLine("Running task {0} with state {1}", Task.CurrentId, o); 
    Thread.Sleep(1000); 
}, -1); 

Task parent = workChain; 
for (int i = 0; i < 10; i++) 
{ 
    parent = parent.ContinueWith((ante, o) => 
     { 
      Console.WriteLine("Running task {0} with state {1}", Task.CurrentId, o); 
      Task subTask = Task.Factory.StartNew(() => 
      { 
       Thread.Sleep(10000); 
       Console.WriteLine("Subtask completed"); 
      }); 
      subTask.Wait(cts.Token); 
     }, i, TaskContinuationOptions.OnlyOnRanToCompletion); 
    parent.ContinueWith((ante) => 
     { 
      foreach (Exception e in ante.Exception.InnerExceptions) 
      { 
       if (e is OperationCanceledException) 
       { 
        //report timeout 
        Console.WriteLine("Timed out while running task id {0}", ante.Id); 
        return; 
       } 
      } 
      //report other exception 
      Console.WriteLine("Something bad happened: {0}", ante.Exception.GetBaseException()); 
     }, TaskContinuationOptions.OnlyOnFaulted); 
} 
Task lastTask = parent.ContinueWith((ante) => 
    { 
     //report success 
     Console.WriteLine("Success"); 
    }, TaskContinuationOptions.OnlyOnRanToCompletion); 
0

使每个可能正在运行的任务正在启动或停止执行的记录。 TPL不记录哪些任务正在运行。