Windows SuspendThread不? (GetThreadContext失败)

时间:2010-08-09 21:04:18

标签: windows multithreading winapi suspend

我们有一个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系统错误代码“拒绝访问”)。

如果没有返回错误,SuspendThread文档似乎清楚地表明目标线程被挂起。我们正在检查SuspendThread和ResumeThread的返回状态;他们永远不会抱怨。

我怎么可以暂停一个线程,但无法访问其上下文?

这个博客 http://www.dcl.hpi.uni-potsdam.de/research/WRK/2009/01/what-does-suspendthread-really-do/

建议SuspendThread返回时可能启动 暂停其他线程,但该线程尚未暂停。在这种情况下,我可以看到GetThreadContext将如何成为问题,但这似乎是一种定义SuspendThread的愚蠢方式。 (如果目标线程实际被挂起,SuspendThread的调用将如何知道?)

编辑:我撒谎。我说这是针对Windows的。

嗯,奇怪的事实是我在Windows XP 64下没有看到这种行为(至少在上周没有这种行为,我真的不知道之前发生了什么)......但是我们一直在测试这个Ubuntu 10.x上的Wine下的Windows应用程序。 Wine source for the guts of GetThreadContext包含。{3}} 当由于某种原因尝试获取线程状态失败时,第819行的访问被拒绝返回响应。我猜,但看起来Wine GetThreadStatus认为线程可能无法重复访问。在SuspendThead超出我之后为什么会这样,但是有代码。想法?

EDIT2:我再次撒谎。我说我们只看到了Wine的行为。不......我们现在发现了Vista Ultimate系统似乎产生了同样的错误(再次,很少)。因此,看起来Wine和Windows就一个模糊的案例达成一致。似乎只是启用Sysinternals进程监控程序会加剧情况并导致问题出现在Windows XP 64上;我怀疑是Heisenbug。 (进程监视器 甚至不存在于Wine-tasting(:-)机器或我用于开发的XP 64系统上。

到底是什么?

EDIT3: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机器上看到这种失败,速度非常低(每隔几千次运行一次)。我们的Suspend活动适用于主要执行纯计算代码但有时会调用Windows的线程。我不记得当线程的PC在我们的计算代码中时看到这个问题。当然,我挂不起线程的PC,因为GetContext不会给我,所以我不能直接确认问题只发生在执行系统调用时。但是,我们所有的系统调用都是通过一个点进行的,到目前为止,有证据表明,当我们处于挂起状态时,该点已被执行。因此间接证据表明,如果该线程正在执行系统调用,则线程上的GetContext将失败。我还没有精力建立一个关键的实验来测试这个假设。

5 个答案:

答案 0 :(得分:3)

让我引用里希特/纳萨雷的“Windows via C++ 5Ed”,这可能会有所启发:

  

DWORD SuspendThread(HANDLE hThread);

     

任何线程都可以调用此函数   暂停另一个线程(只要你   有线程的句柄)。它去了   不说(但我会说   无论如何)一个线程可以暂停   本身但无法恢复自己。喜欢   ResumeThread,SuspendThread返回   线程的先前暂停计数。一个   线程可以暂停多达   MAXIMUM_SUSPEND_COUNT次(已定义   在WinNT.h中为127)。注意   SuspendThread是异步的   尊重内核模式执行,但是   用户模式执行不会发生   直到线程恢复。

     

在现实生活中,申请必须是   当它调用SuspendThread时要小心   因为你不知道是什么   尝试时线程可能正在执行   暂停它。如果线程是   试图从一个分配内存   堆,例如,线程将   在堆上锁定。和其他一样   线程尝试访问堆,   他们的执行将暂停,直到   第一个线程恢复。   SuspendThread只有在您知道的情况下才是安全的   确切地说是目标线程是什么(或   可能会这样做,你采取极端   避免问题或措施的措施   暂停导致死锁   线程。

     

...

     

Windows实际上让你看看里面   一个线程的内核对象并抓住它   当前的一组CPU寄存器。去做   这个,你只需打电话   GetThreadContext:

     

BOOL GetThreadContext(HANDLE   hThread,PCONTEXT pContext);

     

要调用此函数,只需分配一个   CONTEXT结构,初始化一些   flags(结构的ContextFlags   会员)指出哪些注册你   想要回来,并传递地址   GetThreadContext的结构。   然后该功能填写成员   你已经要求了。

     

你应该先调用SuspendThread   调用GetThreadContext;除此以外,   线程可能被安排和   线程的上下文可能不同   从你得到回来。一个帖子   实际上有两个上下文:用户模式   和内核模式。 GetThreadContext可以   仅返回a的用户模式上下文   线。如果你调用SuspendThread   停止一个线程,但该线程是   目前在内核模式下执行,   它的用户模式上下文甚至是稳定的   虽然SuspendThread实际上并没有   暂停线程了。但是   线程不能再执行了   用户模式代码,直到它恢复,所以   你可以安全地考虑线程   暂停和GetThreadContext会   工作

我的猜测是,如果刚刚调用SuspendThread,GetThreadContext可能会失败,而线程处于内核模式,并且内核此时正在锁定线程上下文块。

也许在多核系统上,一个核心是处理用户模式刚刚挂起的线程的内核模式执行,保持锁定线程的CONTEXT结构,正好在另一个核心调用GetThreadContext的时候。

由于没有记录此行为,我建议您与microsoft联系。

答案 1 :(得分:2)

暂停拥有CriticalSection的线程存在一些特殊问题。我现在找不到很好的参考,但有is one mention of it on Raymond Chen's bloganother mention on Chris Brumme's blog。基本上,如果你不幸在线程访问操作系统锁(例如,堆锁,SuspendThread锁等)时调用DllMain,那么真的很奇怪的东西可以发生。我认为这是你很少遇到 的情况。

在处理器收益率GetThreadContext之后重试对Sleep(0)的调用是否有效?

答案 2 :(得分:2)

旧问题,但很高兴看到您在经历了超过2年的问题后仍然保持更新状态更改。

问题的原因是x64版本的WoW64的翻译层存在错误,如下所示:

http://social.msdn.microsoft.com/Forums/en/windowscompatibility/thread/1558e9ca-8180-4633-a349-534e8d51cf3a

在WoW64下的GetThreadContext中存在一个相当严重的错误,它使得它返回陈旧的内容,这使得它在许多情况下无法使用。内容存储在用户模式中这就是为什么你认为值不是-null,但是在陈旧的内容中它仍然是null。

这就是为什么它在较新的操作系统上失败但不在较旧的操作系统上失败,尝试在Windows 7 32位操作系统上运行它。

至于为什么在使用Visual Studio 2010/2012构建的解决方案时,这个bug似乎不那么频繁发生,很可能是编译器正在做的事情正在缓解大部分问题,为此你应该检查从2005年和2010年,看看有什么不同。例如,如果在没有优化的情况下构建项目,问题是否会发生?

最后,进一步阅读:

http://www.nynaeve.net/?p=129

答案 3 :(得分:0)

可能是线程安全问题。你确定hComputeThread结构不会从你下面改变吗?当你叫暂停时,也许线程正在退出?这可能会导致挂起成功,但是当你调用get context时,它就会消失并且句柄无效。

答案 4 :(得分:0)

在拥有同步对象的主题上调用 SuspendThread ,例如互斥关键部分,如果调用线程尝试获取挂起线程拥有的同步对象,则可能导致死锁。 - MSDN