【问题标题】:Killing an *unresponsive* thread in Android/Java在 Android/Java 中杀死一个 *unresponsive* 线程
【发布时间】:2012-05-25 14:10:09
【问题描述】:

我知道优雅地关闭线程的公认正确解决方案。

但是假设我的游戏应该是容错的,当一个新的游戏开始时,它会尝试优雅地关闭旧游戏的(现在暂停的)线程。如果它无法加入/关闭它(例如,因为旧线程有问题并且处于无限循环中),它会实例化新线程并启动它。但我不喜欢旧线程仍然存在并吃资源的事实。

是否有一种公认的方法可以在不终止进程的情况下终止无响应的线程?在我看来,事实上,我在某处读到 Thread 也可能不会对 Thread.stop() 做出反应。

所以没有办法处理无限循环中的线程(例如由于错误),是吗?即使它对 Thread.stop() 做出反应,文档说 Thread.stop() 可能会使 Dalvik VM 处于不一致状态...

【问题讨论】:

    标签: android multithreading infinite-loop


    【解决方案1】:

    如果您需要此功能,则必须对其进行设计和实施。显然,如果你不设计和实现优雅的方式来关闭线程,那么就没有办法优雅地关闭线程。没有通用的解决方案,因为解决方案是特定于应用程序的。例如,这取决于线程可能持有哪些资源以及线程可能持有哪些锁或已损坏的共享状态。

    标准答案是:如果您需要此功能,请不要使用线程。使用进程。

    核心原因是线程的工作方式。您获取锁,然后操作共享数据。在您操作该共享数据时,它可能会进入不一致的状态。在释放锁之前将数据恢复到一致状态是线程的绝对责任。 (例如,从双向链表中删除一个对象。您必须先调整正向链接或反向链接。在这两个操作之间,链表处于不一致状态。)

    假设你有这个代码:

    1. 获取锁或进入同步块。

    2. 开始修改锁保护的共享状态。

    3. 错误

    4. 将锁保护的数据恢复到一致状态。

    5. 释放锁。

    那么,现在,我们该怎么办?在第 3 步,线程持有一个锁,它遇到了错误并触发了异常。如果我们不释放它在步骤 1 中获得的锁,那么每个试图获得相同锁的线程都将永远等待,我们注定要失败。如果我们确实释放了它在第 1 步中获得的锁,那么每个获得锁的线程都会看到该线程未能清理的不一致共享状态,因为它从未进入第 4 步。无论哪种方式,我们都注定要失败。

    如果线程遇到应用程序程序员没有创建合理的处理方式的异常情况,则该进程注定失败。

    【讨论】:

    • 但问题正是容错:即无论出于何种原因(错误),线程进入了无限循环,我们需要一个新线程(可以杀死旧线程)。开发者不设计bug,bug就来了这有效,不会让虚拟机处于不一致的状态。到那时,我自己的线程使用的变量是不必要的,所以这些变量没有被重用,但 Android 文档也提到了虚拟机......
    • 当我写“我们需要一个新线程”的时候,我的意思是开始了一个新的游戏玩法,所以不需要旧线程,但应该清除它以清理资源(它可以'不能优雅地完成,因为它由于编程错误而处于无限循环中)。
    • @ThomasCalc:“我如何编写代码来正确处理我没有编写代码来正确处理的情况?”显然,你不能。线程可以访问所有进程资源,因此不能假定没有进程资源是未受污染的。线程之间没有墙(这就是我们使用它们的原因),所以如果一个线程损坏了,进程中的所有线程都是。您需要一个新流程。
    • 在本机代码中,您可以完全访问用于终止线程和进程的 POSIX 调用(当然,取决于进程权限)。因此,您可以违反 Java/Dalvik 层对杀死线程的限制。但当然要靠你自己!
    • @ThomasCalc:是的,正是我的意思。 JVM 无法正确释放锁,因为它无法知道如何清理锁保护的数据结构。一旦线程遇到应用程序代码不知道如何优雅地清理的错误情况,进程上下文(以及其中的所有其他线程)应该被认为是损坏的。 (我将更新我的答案以更详细地解释这一点。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 2012-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多