在执行过程中,任务状态不会更改为已取消

时间:2017-12-06 12:54:18

标签: c# task task-parallel-library cancellationtokensource

我正在阅读Parallel Programming上的一本书,我编写了以下方案,该方案按预期工作(Task.Status == TaskStatus.Canceled):

  

任务可以在TaskStatus.Canceled中结束。为了这   要发生,你必须将CancellationToken作为参数传递给   创建任务的工厂方法。如果该令牌发出请求信号   要在任务开始执行之前取消,任务将不会   允许跑。任务的Status属性将直接转换为   TaskStatus.Canceled没有调用任务的用户委托。

但我无法获得以下预期结果(Task.Status!= TaskStatus.Canceled):

  

如果令牌在任务开始后发出取消请求   执行时,任务的Status属性只会转换为   TaskStatus.Canceled如果用户委托抛出一个Operation   CanceledException和该异常的CancellationToken属性   包含创建任务时给出的令牌。

代码

  public void DoWork(int millisec, CancellationToken token, int n )
    {
        ParallelOptions option = new ParallelOptions { CancellationToken = token };

        Parallel.For(0, n, option, (i) =>
        {
           // Thread.Sleep(millisec);
            token.ThrowIfCancellationRequested();
        });
    }

private void btnSearch_Click(object sender, EventArgs e)
{                        
   CancellationToken token = cts.Token;

   t1 = Task.Factory.StartNew(() => DoWork(700, token, 99999999), token);
   t2 = Task.Factory.StartNew(() => DoWork(5, token, 1), token);

   Task[] tasks = new Task[] { t1,t2 };

   int completedTaskIndex = Task.WaitAny(tasks);
   lblMsg.Text = "Task No." + ++completedTaskIndex + " has been completed.\n";

   cts.Cancel();
        Thread.Sleep(5000); // Updated
   lblMsg.Text += "Rest of the tasks have been cancelled.\n";

   try
   {
       lblMsg.Text += "Task 1 Status: " + t1.Status + "\n";
       lblMsg.Text += "Task 2 Status: " + t2.Status + "\n";

       Task.WaitAll(tasks);
       lblMsg.Text += "No exceptions has been reported yet.\n";
   }
   catch(AggregateException ae)
   {

           ae.Flatten().Handle((ex) =>
           {
          // if you debug to see the token handle it is the same as passed to Task.Factory.StartNew()
               if (ex is OperationCanceledException)
           {
               CancellationToken tok = ((OperationCanceledException)ex).CancellationToken;
               return true;
           }
           else
           {
               return false;
           }
       }
       );
   }
   finally 
   {
     if(cts!= null)  
         cts.Dispose();

     cts = null;
   }
}

结果

更新前:

enter image description here

更新后:

enter image description here

Dicussion

任务1是被取消的任务,因此其状态应该已被取消而不是正在运行,如上图所示。

问题

是否有任何理解上的差距,我无法得到理想的结果?我在哪里失去了赛道?

已更新(已解决问题)

问题是我在更新为Canceled之前正在读取任务1的状态。所以我写了Thread.Sleep(5000);就在cts.Cancel()之后;这样就可以有足够的时间取消任务并适当更新状态。这让任务1的状态显示"已取消"。

1 个答案:

答案 0 :(得分:0)

在评论中看一下程序流程,假设处理每一行需要1ms:

   int completedTaskIndex = Task.WaitAny(tasks); //Time T=0
   lblMsg.Text = "Task No." + ++completedTaskIndex + " has been completed.\n"; //time T=5, because we waited 5ms for task 2, right?

   cts.Cancel(); //Time T=6, task 1 is sleeping for another 694 ms
   lblMsg.Text += "Rest of the tasks have been cancelled.\n"; //Time T=7

   try
   {
       lblMsg.Text += "Task 1 Status: " + t1.Status + "\n";//Time T = 8, task1 is sleeping for another 692 ms, current status is "running"
       lblMsg.Text += "Task 2 Status: " + t2.Status + "\n";//Time T=9

       Task.WaitAll(tasks);//Time T=701, task 1 has finished sleeping, and finds out it is cancelled
       lblMsg.Text += "No exceptions has been reported yet.\n";//we probably don't hit this line/see it in label, because cancellation exception is thrown by task 1
   }
   catch(AggregateException ae) //what's task1's status now?

最终我认为这是一个例子,如果你对现实世界的任务进行编码,那么在开始你的千分贝查询之前,有一个“go”按钮变为“取消”的用户界面,事情本来就会成功..但是你安排的这种人为测试并不能真正代表现实生活取消场景