首页 > 解决方案 > 获取/检查进程 win32 的内部 kernel32 状态(为了安全使用 TerminateThread )

问题描述

我已经为用户可用的线程编写了一个带有终止选项的线程池。如 API 文档中terminateThread()所述,

如果目标线程在终止时正在执行某些 kernel32 调用,则线程进程的 kernel32 状态可能不一致。

我可以自己验证这个问题:在这种情况下终止线程会导致内存分配问题(以及其他问题),但修复该条件同时解决了问题。

问题

  1. 所以,我想在每次terminateThread()使用后检查这个内部状态。如果terminateThread()在 kernel32.dll 中导致进程的内部状态出现问题,我想引发异常 - 并在登录到用户后终止进程(除非仍然可以修复内部状态)。

    这可行吗?也许通过找到相关状态变量的地址(或类似的东西 - 通过匹配 kernel32 的 pdb 文件或其他方式)?这种情况对我来说很糟糕——如果我无法解决它,我要么必须省略 threapool 的终止选项,要么只将线程留给自己。任何提示将不胜感激!

  2. 是否还有其他导致类似问题的win32功能?

  3. 一种。当它调用一个绝对永远不会返回的阻塞 kernel32 函数时,为它自己留下一个线程是否安全?

    湾。如果 win32 函数返回而 lambda 函数已被销毁会怎样?

我为什么要问这个?(补充资料)

我的项目中有一个自定义线程池,我在其中调用了一些有时可能会永远阻塞的 win32 API。因此,我使用超时来调用它们。当达到超时时,我调用terminateThread()并让我的线程池返回“不成功的调用状态”。

有时,我当前的应用程序会陷入僵局。我发现这个死锁发生在线程池中,所以我正在寻找替代方案terminateThread()(例如按照我在问题中描述的那样离开线程)或尝试修复内部状态,或者至少验证是否terminateThread()是根我的僵局。

我也想在其他项目中重用这个线程池,所以我应该让它安全。

更新:问题已修复。

我在我的应用程序中发现了错误:它实际上是terminateThread()在我的线程池中的超时已经很低(大约 200 毫秒)时调用。线程在它没有阻塞的时候被杀死(即,如果有更长的超时时间,它会工作并正确返回)。从内核堆栈跟踪中,我发现在内核模式下,一个互斥锁在线程终止时被锁定,而当线程退出时,其他线程已经在等待该互斥锁。

通过将最小超时时间增加到 1000 毫秒,问题首先似乎消失了,但我对此并不满意:我的解决方案是在达到超时时在堆上创建 lambda,将 lambda 和线程留给自己而不终止,并且将其添加到_ToTerminateThreads. 该列表每 10 分钟终止一次(等待 10 分钟,复制列表,再等待一分钟,然后终止线程并删除 lambdas)。

尽管如此,经过测试和数小时的调试后,我还是遇到了堆损坏。最后我发现了以下内容:我留待删除的线程写入了用户函数(已传递给线程池)使用的内存 - 由于线程池已返回,它们被释放。这导致了最终的问题,因此最终的解决方案是将超时增加到安全量。

我建议所有需要这种功能的人将其部署到子进程,并终止该进程而不是使用线程。

我保持这个问题是开放的,因为主要的 4 个问题还没有得到回答。对于我的问题,我不再需要他们的答案,但他们可能对 stackoverflow 的其他成员很感兴趣。

标签: c++winapithread-safetythreadpoolkernel32

解决方案


我的问题已解决,尽管它与帖子中的 3 个问题无关。我尝试以相反的顺序回答它们:

  • ad 3.b.)如果一个外部函数返回并且您的本地 lambda 已被删除,cpu 将不知道这一点,并将尝试将该偏移处的字节作为 CPU 指令处理。这肯定会搞砸你,所以永远不要那样做!

  • ad 3.a.)是的,如果你 100% 确定外部函数永远不会返回,那么离开是安全的(否则返回时会弄乱你的应用程序

    1. 如果您使用b 中解释的相同方式删除了其余代码。
    2. 如果您没有删除 lambda 或者它是一个 gobal 函数,它将运行其余函数,这些函数可能正在编辑已被释放并导致堆损坏的动态分配内存(堆,而不是堆栈),或者只是编辑一些全局变量)。
  • 广告 2.)我搜索了危险的 winapi 函数,除了TerminateThread(). 如果您知道一个,请添加评论或写另一个答案。

  • 广告 1.)我没有任何解决方案来检查/修复 Microsoft 所指进程的内部 kernel32 状态。我认为阅读过 kernel32.dll 源代码的微软人应该回答这个问题。

除了这个 kernel32 状态之外,TerminateThread()还会导致许多其他问题(如资源/堆分配、互斥锁、泄漏等),所以除非你 100% 确定你在做什么,否则永远不要使用它。

阅读评论中链接的文章@RichardCritten:TerminateThread()

我的代码中的错误是什么?

我打电话TerminateThread()的超时时间很短(300 毫秒)。随机地,当机器资源不足时,该功能仍在运行(我的意思是非阻塞调用!)。我终止了该函数,从而导致内核互斥锁被锁定。这个锁定的互斥锁使所有其他线程等待 - 当它们返回时不会退出。

评论

在没有收到任何答案后,我根据发现的内容回答了我自己的问题。因此,它可能包含一些错误信息。如果这有什么问题,请纠正我。


推荐阅读