所以我在过去几个月一直在玩线程,虽然我的输出是预期的,但我有一种感觉,我不是这样做的最佳方式。我似乎无法从任何与我合作的人那里得到一个直接的答案,我认为我会问你们。
问题:我会试着让这个简单,所以请耐心等待。假设我有一个具有开始和停止按钮的表单。触发启动按钮和启动线程的事件。在这个帖子的DoWork
里面,它会调用3个方法。 Method1()打印到控制台“A \ n”10次,中间暂停10秒。 Method2()和Method3()完全相同,只是在Console.WriteLine
之间的不同字母和不同的暂停时间。现在,当您按下停止按钮时,您希望立即响应。我不想等待方法完成。我该怎么做?
我这样做的方法是将我的BackgroundWorker传递给每个方法并检查worker.CancellationPending就像这样
public void Method1(BackgroundWorker worker)
{
for(int i = 0; i < 10 && !worker.CancellationPending; ++i)
{
Console.WriteLine("A");
for(int j = 0; j < 100 && !worker.CancellationPending; ++i)
{
Thread.Sleep(100);
}
}
}
就像我说的那样给了我想要的结果然而想象一下,method1变得复杂得多,假设它使用的是一个带有keydown和key的DLL。如果我只是中止线程,我可能会让自己处于不受欢迎的状态。我发现自己用!worker.CancellationPending
乱丢我的代码。实际上每个代码块都在检查CancellationPending。我在网上看了很多例子,我很少看到人们像我一样传递一个线程。这方面的最佳做法是什么?
答案 0 :(得分:1)
考虑使用迭代器(yield return)来分解步骤。
public void Method1(Backgroundworker worker)
{
foreach (var discard in Method1Steps)
{
if (worker.CancelationPending)
return;
}
}
private IEnumerable<object> Method1Steps()
{
for (int i = 0; i < 10; ++i)
{
yield return null;
Console.WriteLine("A");
for (int j = 0; j < 100; ++i)
{
Thread.Sleep(100);
yield return null;
}
}
}
如果你有一堆try / catch / finally或一堆方法调用需要了解取消,这个解决方案可能更难实现。
答案 1 :(得分:0)
是的,你正确地做到了。起初看起来可能很尴尬,但它确实是最好的选择。它绝对比中止一个线程要好得多。正如您所发现的,循环迭代是检查CancelationPending
的理想候选者。这是因为循环迭代通常会隔离逻辑工作单元,从而轻松描绘安全点。安全点是执行线程的标记,可以在不破坏任何数据的情况下轻松完成终止。
诀窍是经常在安全点轮询CancelationPending
,以便及时向呼叫者反馈取消成功完成,但不要太频繁地对性能产生负面影响或“乱丢代码”。
在您的特定情况下,内部循环是轮询CancelationPending
的最佳位置。我会省略对外循环的检查。原因是因为内循环是大部分时间花费的地方。对外循环的检查是没有意义的,因为外循环除了使内循环运行外几乎没有实际工作。
现在,在GUI端,您可能希望将停止按钮变灰,以便让用户知道取消请求已被接受。您可以显示“取消等待”等消息以使其清晰。一旦您收到取消已完成的反馈,您就可以删除该消息。
答案 2 :(得分:0)
好吧,如果您处于必须中止CPU密集型线程的情况下,那么您在一个循环或另一个循环中测试“Abort”布尔值(或取消令牌)时会感到困惑(可能不是最里面的 - 取决于这需要多长时间)。 AFAIK,您可以从内循环“返回”,因此退出方法 - 无需在每个级别进行检查!为了最大限度地减少开销,请尝试将其设置为local-ish布尔值,即尽量不要通过半打......类来取消引用它。
Maybee继承了'Stoppable'中的类,它有'Abort'方法和'Stop'布尔值?上面的示例线程花费大部分时间休眠,因此在检查任何内容之前,您将获得50ms的平均延迟。在这种情况下,您可以等待某个事件超时而不是休眠。覆盖'Abort'以设置事件以及调用继承的Abort&amp;所以尽早终止等待。如果你实现Dan所描述的这个新功能,你也可以在cancellationToken委托/回调中设置事件。
实际上很少有Windows API等不容易“不可操作”或没有异步“Ex”版本,所以它是错误的......“几乎”总是可以取消,无论如何,例如。关闭套接字以强制读取套接字除外,写入临时文件以强制文件夹更改通知返回。
RGDS, 马丁