一个非常简单的方法。 两个线程。
volatile __int32 p=0;
一个线程(A)仅使用
while(1){
ExecuteAVeryCPUIntesiveThing();
InterlockedExchange(&p, 0);
}
另一个线程(B)使用
while(1){
if(0==InterlockedCompareExchange(&p,0,0))
InterlockedExchange(&p, 1);
}
如果有人尝试在处于压力下的系统下记录这一点。 (大量内存交换、io、套接字、cpu 峰值..) A 中的值不会传播到 B。
在 A 中,p 的值为 0。 但从 B 的角度来看,p 一直停留在 1。 在我的世界中,当 A 将值设置为 0 时,B 应该检测到并将该值设置为 0。 在实际硬件上,这就像那样工作,但在 esxi 上运行时则不然。
这似乎在真实硬件和某些虚拟系统下工作正常,但在 vmware 下则不然。
我是不是出现了思维失误或者……?
操作系统访客:win2008服务器
使用适用于 x64 的 Microsoft (R) C/C++ 优化编译器版本 15.00.30729.01 编译的代码
主机:esxi 4.1
更新:
回复评论:是的,它会在 0 和 1 之间反弹, 但正如所写,线程 B 不会反弹,因为 p 的值在 B 的视角中永远不会/或不会改变,它会反弹 10-20 次然后停止。
我只想在非常精确的时刻执行 A 中的代码块( ExecuteAVeryCPUIntesiveThing();)。
生产代码充满了更多线程和事件、互斥锁和锁,但事实仍然是,如果我剥离并仅使用上述代码,如果我在 guest 操作系统上生成大量 cpu、mm、io,我可以重现它.
最佳答案
此代码是等待发生的线程竞争。您可能从 VMWare 获得它,因为您只为虚拟机分配了一个处理器。
该代码缺少的是一个互锁,该互锁可确保线程 B 看到 p
的值发生变化。因此,如果线程 A 获得了一个 cpu 核心并保持运行一段时间,而线程 B 被阻塞,等待一个时间片运行,那么线程 A 可以多次将 p
设置为 0。线程 B 将不会注意到这一点,因为它从未有机会将 p
设置回 1。
您需要重新考虑您的锁设计。这个问题不仅限于VMWare,在普通机器上也可能出错,只是可能性小得多。每个月都会出错一次,或多或少,不可能调试。否则,这是一种经典的生产者/消费者场景,您可以使用线程安全队列来解决该场景。
关于c - InterlockedExchange 在 vmware 下失败,有解决办法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10383022/