gpt4 book ai didi

c# - 为什么在调用取消后线程继续运行?

转载 作者:太空宇宙 更新时间:2023-11-03 17:19:41 24 4
gpt4 key购买 nike

考虑这个简单的例子code :

var cts = new CancellationTokenSource();
var items = Enumerable.Range(1, 20);

var results = items.AsParallel().WithCancellation(cts.Token).Select(i =>
{
double result = Math.Log10(i);
return result;
});

try
{
foreach (var result in results)
{
if (result > 1)
cts.Cancel();
Console.WriteLine($"result = {result}");
}
}
catch (OperationCanceledException e)
{
if (cts.IsCancellationRequested)
Console.WriteLine($"Canceled");
}

Foreach 并行结果中的结果,它会打印结果直到 result > 1
此代码输出类似于:
result = 0.9030899869919435
result = 0.8450980400142568
result = 0.7781512503836436
result = 0
result = 0.6020599913279624
result = 0.47712125471966244
result = 0.3010299956639812
result = 0.6989700043360189
result = 0.9542425094393249
result = 1
result = 1.0413926851582251 <-- This is normal
result = 1.2041199826559248 <-- Why it prints this value (and below)
result = 1.0791812460476249
result = 1.2304489213782739
result = 1.1139433523068367
result = 1.255272505103306
result = 1.146128035678238
result = 1.2787536009528289
result = 1.1760912590556813
result = 1.3010299956639813
Canceled

我的问题是为什么它继续打印超过 1 的值? 我原以为它是 Cancel() token 将终止进程。

更新 1

@mike-s的回答建议:

It's also useful to check a cancellation token inside a loop (as a means to abort the loop) or before a long operation.



我试过添加支票
foreach (var result in results)
{
if (result > 1)
cts.Cancel();

if (!cts.IsCancellationRequested) //<----Check the cancellation token before printing
Console.WriteLine($"result = {result}");
}

它仍然给出相同结果的输出。

最佳答案

My question is why it continue printing values over 1?



想象一下,你雇了一百名飞行员从一百个机场驾驶一百架飞机。一大堆起飞,然后你发一条消息说“取消所有航类”。好吧,当您发送该消息时,跑道上有一堆飞机以起飞速度起飞,并且消息在它们起飞后到达。这些航类不会被取消!

您正在发现关于多线程编程最重要的知识。 你必须推理,好像所有可能发生的事情都可能发生 .这包括比您认为应该迟到的消息。

特别是,您的问题是您滥用并行化机制的结果,这些机制旨在并行化长时间的工作。您创建了一堆任务,这些任务的运行时间比发送消息停止它们所需的时间要少。在这种情况下,某些任务在被告知停止后完成也就不足为奇了。

I expected that calling Cancel() on the token would terminate the process.



你的期望是完全错误的。停止期望,因为这种期望绝不符合现实。取消 token 是在方便时立即取消操作的请求。它不会终止线程或进程。

但是,即使您确实终止了线程,您仍然会观察到这种行为。线程终止与任何其他事件一样是一个事件,并且该事件不是即时的。执行需要时间,并且其他线程可以在执行该线程终止时继续其工作。

what do you mean by "convenient" in "a request to cancel an operation as soon as it is convenient to do so"?



让我们退后一步。

如果要完成的工作非常短,那么就没有必要将其表示为一项任务。干活!一般来说,如果工作花费的时间少于 30 毫秒,就去做。

因此,我们假设每个任务都需要很长时间。

现在,为什么一项任务可能需要很长时间?一般有两个原因:
  • 我们正在等待另一个系统完成某些任务。我们正在等待网络数据包或磁盘读取或类似的事情。
  • 我们有大量的计算,CPU 已经饱和。

  • 假设我们处于第一种情况。 并行化有帮助吗? 不会。如果您在等待邮寄包裹,雇佣一、二、十或一百人等待不会使包裹更快送达。

    但这确实有助于第二种情况;如果我们在机器中有一个额外的 CPU,我们可以使用两个 CPU 在大约一半的时间内解决问题。

    因此我们可以假设,如果我们正在并行化一个任务,那是因为 CPU 正在做很多工作。

    伟大的。现在,“CPU 做了很多工作”的本质是什么?它几乎总是在某处涉及一个循环。

    那么,我们如何取消一个任务呢?我们不会通过终止线程来取消任务。我们要求任务自行取消。一个设计良好的任务将接受一个取消标记,并在其循环中检查取消标记是否指示该任务已取消。 取消合作 .该任务必须合作并决定何时检查它是否被取消。

    请注意,检查您是否被取消是一项工作,而且这项工作会占用实际任务的时间。如果您花一半的时间检查您是否被取消,那么您的任务将花费两倍的时间。请记住,并行化任务的目的是使其花费一半的时间,因此将完成任务所需的时间加倍是不可能的。

    因此,大多数任务不会每次都通过循环检查它们是否被取消。精心设计的任务将每隔几毫秒检查一次,而不是每隔几纳秒检查一次。

    这就是我所说的“取消是在方便时要求停止”的意思。如果编写正确,该任务应该知道检查取消的好时机,以便平衡响应能力和性能。

    关于c# - 为什么在调用取消后线程继续运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59075637/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com