我正在阅读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;
}
}
结果
更新前:
更新后:
Dicussion
任务1是被取消的任务,因此其状态应该已被取消而不是正在运行,如上图所示。
问题
是否有任何理解上的差距,我无法得到理想的结果?我在哪里失去了赛道?
已更新(已解决问题)
问题是我在更新为Canceled之前正在读取任务1的状态。所以我写了Thread.Sleep(5000);就在cts.Cancel()之后;这样就可以有足够的时间取消任务并适当更新状态。这让任务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”按钮变为“取消”的用户界面,事情本来就会成功..但是你安排的这种人为测试并不能真正代表现实生活取消场景