如何安全地停止正在运行的线程?

10

我有一个线程,其中包含执行IronPython脚本的操作。由于某些原因,我可能需要随时停止该线程,包括脚本执行。如何实现这一点?第一个想法是使用Thread.Abort(),但是它被认为是不好的做法...


是的,突然终止线程是不好的。但如果这正是你想要做的,那么 Thread.Abort() 是可行的方法。更好的解决方案是修改应用程序的设计。 - Cody Gray
你的线程是循环运行还是仅在执行单个脚本时运行一次就结束? - M.Babcock
它会循环,但每次迭代都包含脚本执行,这可能会相当漫长。 - aplavin
@CodyGray,您所说的修改设计是什么意思? - aplavin
为了让你不必“随时停止线程”。问题中没有提供足够的上下文,因此我无法给出更具体的建议。我的观点是,你对Thread.Abort不理想的看法是正确的,但那个函数并不邪恶,邪恶的是它所做的事情。既然它正好做到了你想要的... - Cody Gray
2个回答

19
什么是安全停止一个正在运行的线程的方法?
将该线程置于其自己的进程中。当您想要停止它时,结束该进程
这是唯一安全的杀死线程的方法。强制终止线程可能会严重破坏进程并丢失用户数据。如果您确实需要能够杀死可能在执行任何任务的线程,则无法避免“丢失用户数据”的情况。唯一可避免使调用强制终止的进程不稳定的方法是将它们做成 完全不同的进程

在我的情况下,线程代码没有办法破坏整个进程。但是就常识而言——通过创建不同的进程,您的意思是有两个编译为不同程序集的项目,其中一个运行另一个?还是什么? - aplavin
1
@chersanya:你似乎对自己非常有把握。你正在运行一个脚本;你怎么知道脚本的作者没有在其中放置会破坏进程或在中断时丢失用户数据的代码? - Eric Lippert
@chersanya,不同的进程不一定需要运行不同的程序集。例如,myprogram.exe 可以启动一个新进程来运行 myprogram.exe - phoog
@Eric,这可能是真的,但也有一个缺点。一般来说,你不想随便杀死进程。相反,你想发送一个信号或消息告诉它们停止。如果该进程正在管理某些资源,那么这非常重要,因此它可以将资源恢复到一致状态。对于某些资源(如文件锁或信号量),操作系统可以恢复它们(可能会暴露其他资源处于不一致状态),但并非所有资源都可以这样做,而且副作用通常是不希望发生的。 - Kevin Cathcart
为了让它符合您的建议,请向新进程发送消息,该进程具有一个“额外”的线程,仅查找消息,尽可能使资源一致,然后关闭自己的进程,从而停止其中运行的其他线程。 - Kevin Cathcart
5
@KevinCathcart: 好的,当然。但是如果你有能力优雅地控制“流程”,那么你肯定也有能力优雅地控制“线程”。如果你可以构建一个控制系统,那么尽管去做;我理解这个问题是:“但如果我无法控制代码,但我仍然想让它立刻停止,我该怎么办?” - Eric Lippert

5

根据你的问题和后续评论,我可以向你建议两个选项,但需要提醒以下:

  1. If your thread loops executing something on each iteration, you can set a volatile boolean flag such that it exits after finishing the current iteration (pseudocode because I'm not familiar with python):

    while shouldExit = false
        // do stuff
    

    Then just set the flag to true when you want the thread to stop and it will stop the next time it checks the condition.

  2. If you cannot wait for the iteration to finish and need to stop it immediately, you could go for Thread.Abort, but make absolutely sure that there is no way you can leave open file handles, sockets, locks or anything else like this in an inconsistent state.


1
第一个解决方案很明显,但不适用于我的情况。我害怕使用Thread.Abort(),因为到处都有人写着类似“永远不要使用它!!!”的东西。但现在我在程序中使用它了 - 处理程序可能会保持打开状态。 - aplavin
2
虽然大多数情况下线程是不好的,但具体原因是因为人们倾向于将线程用于I/O或其他类似的事情,如果线程被终止,这些事情将处于不一致的状态。如果您没有将线程用于此类操作,则完全安全。 - Tudor
这段代码中的线程在调用 Abort() 后是否保证会停止? try { // do work - no try/catches here } catch(ThreadAbortException) { // log exit } - aplavin
除非您在try块内部被某些I/O阻塞,否则它应该正常工作。 - Tudor
1
@chersanya:绝对不行。这根本不能保证。特别是,如果你试图中止的线程中运行着敌对代码,那么你已经输了;敌对代码可以忽略线程中止。中止线程应该被视为对线程的强烈建议,让它离开;一个对此有敌意的线程可以在其巧妙编写的情况下随意忽略它。 - Eric Lippert
4
进一步谈论您的优秀观点:你知道什么比保持文件句柄打开更糟糕吗?当只有用户数据的一半已被缓冲出来时关闭文件句柄,导致文档处于损坏状态,以至于用户的数据无法恢复。 锁也是如此;离开监视器处于进入状态是不好的。在完成对关键用户数据的受保护编辑的代码之前退出监视器更糟糕。人们认为中止线程很糟糕,因为它会留下分配的句柄,但实际问题是它经常释放得太早 - Eric Lippert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接