我们有一个Windows32应用程序,其中一个线程可以停止另一个线程来检查
通过执行suspendthread/getthreadcontext/resumethread来声明[pc等]。
if (SuspendThread((HANDLE)hComputeThread[threadId])<0) // freeze thread
ThreadOperationFault("SuspendThread","InterruptGranule");
CONTEXT Context, *pContext;
Context.ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL);
if (!GetThreadContext((HANDLE)hComputeThread[threadId],&Context))
ThreadOperationFault("GetThreadContext","InterruptGranule");
在多核系统上,getthreadcontext很少返回错误代码5(windows系统错误代码“access denied”)。
SuspendThread文档似乎清楚地表明,如果没有返回错误,目标线程将被挂起。我们正在检查suspendthread和resumethread的返回状态;他们从来没有抱怨过。
为什么我可以挂起一个线程,却不能访问它的上下文?
这个博客
http://www.dcl.hpi.uni-potsdam.de/research/WRK/2009/01/what-does-suspendthread-really-do/
表示SuspendThread在返回时可能已启动
另一个线程的挂起,但该线程尚未挂起。在这种情况下,我可以看到getthreadcontext是如何有问题的,但这似乎是一种定义suspendthread的愚蠢方法。(suspendthread的调用如何知道目标线程何时实际挂起?)
编辑:我撒谎了。我说这是窗户用的。
好吧,奇怪的事实是,我在windows xp 64下没有看到这种行为(至少在上周没有看到,我也不知道之前发生了什么)。但是我们已经在ubuntu 10.x上用wine测试了这个windows应用程序。
当试图获取线程状态因某种原因失败时,第819行上的拒绝访问返回响应。我猜,但wine getthreadstatus似乎认为线程可能无法重复访问。我不明白为什么在一次暂停之后会是这样,但有密码。思想?
伊迪丝2:我又撒谎了。我说我们只看到酒的行为。不。。。我们现在发现了一个vista终极系统,它似乎会产生同样的错误(同样,很少)。所以,看来葡萄酒和窗户在一个模糊的案例上是一致的。似乎仅仅启用sysinternals进程监视器程序就加重了这种情况,并导致问题出现在windows xp 64上;我怀疑是heisenbug。(过程监视器
甚至在我用来开发的品酒机或xp 64系统上都不存在。
到底是什么?
编辑3:2010年9月15日。我已经为suspendthread、resumethread和getcontext添加了对错误返回状态的仔细检查,而不会干扰代码。从那以后,我就再没有在windows系统上看到过这种行为的任何迹象。还没回到葡萄酒实验。
2010年11月:奇怪。如果我在visualstudio 2005下编译它,它在windows vista和7上会失败,但在早期的操作系统上不会。如果我在visualstudio 2010下编译,它在任何地方都不会失败。有人可能会指着visualstudio2005,但我怀疑是位置敏感的问题,vs 2005和vs 2010中的不同优化器将代码放在稍微不同的位置。
2012年11月:传奇还在继续。我们在许多xp和windows 7机器上都看到了这种故障,其发生率非常低(每几千次运行一次)。我们的挂起活动应用于线程,这些线程主要执行纯计算代码,但有时会调用windows。我不记得当线程的pc在我们的计算代码中时看到过这个问题。当然,当线程挂起时,我看不到它的pc,因为getcontext不会把它给我,所以我不能直接确认问题只在执行系统调用时发生。但是,我们所有的系统调用都是通过一个点进行的,目前为止的证据是,当我们得到挂起的时候,这个点被执行了。因此,间接证据表明,只有当线程正在执行系统调用时,线程上的getcontext才会失败。我还没来得及做一个关键的实验来验证这个假设。
最佳答案
让我引用里希特/纳斯萨尔的“AA>”,这可能会有所启发:
dword suspendthread(handle hthread);
任何线程都可以调用此函数
挂起另一个线程(只要您
有线柄)。它走了
不用说(但我会说的
无论如何)线程可以挂起
但无法恢复。喜欢
ResumeThread,SuspendThread返回
线程以前的挂起计数。一
线程可以挂起多达
最大挂起次数(定义
在温特郡127号)。请注意
SuspendThread与
关于内核模式执行,但是
不执行用户模式
直到线程恢复。
在现实生活中,应用程序必须
调用SuspendThread时要小心
因为你不知道
当你试图
暂停它。如果线程是
试图从
例如,heap线程将
把锁放在堆上。作为其他
线程试图访问堆,
他们的死刑将暂停执行直到
第一个线程被恢复。
SuspendThread只有在你知道的情况下才是安全的
目标线程是什么(或
可能正在做)而且你采取极端
避免问题或
暂停
线。
…
Windows实际上让您可以查看内部
线程的内核对象并获取其
当前CPU寄存器集。做
这个,你只要打个电话
GetThreadContext:获取线程上下文:
bool getthreadcontext(句柄
hthread、pcontext和pcontext);
要调用此函数,只需分配
上下文结构,初始化一些
标志(结构的ContextFlags
成员)指示您注册的
想回去把地址传给我
到getThreadContext的结构。
然后,函数将填充成员
你已经要求了。
你应该先调用SuspendThread
调用getThreadContext;否则,
线程可能被调度,并且
线程的上下文可能不同
从你得到的。线
实际上有两个上下文:用户模式
以及内核模式。GetThreadContext可以
仅返回
线。如果调用suspendthread
停止一个线程,但该线程是
当前在内核模式下执行,
它的用户模式上下文甚至是稳定的
虽然SuspendThread实际上没有
挂断了线。但是
线程无法再执行
用户模式代码,直到它被恢复,所以
你可以放心地考虑
Suspended和GetThreadContext将
工作。
我的猜测是,如果您刚刚调用了suspendthread,而线程处于内核模式,并且内核此时正在锁定线程上下文块,那么getthreadcontext可能会失败。
也许在多核系统中,一个内核正在处理用户模式刚刚挂起的线程的内核模式执行,并在另一个内核调用getthreadcontext时保持锁定线程的上下文结构。
由于这种行为没有记录在案,我建议联系微软。