2

一个非常简单的方法。两条线。

volatile __int32 p=0;

一根线 (A) 只使用

while(1){
    ExecuteAVeryCPUIntesiveThing();
    InterlockedExchange(&p, 0);
}

另一个线程 (B) 使用

while(1){
    if(0==InterlockedCompareExchange(&p,0,0))
        InterlockedExchange(&p, 1);
}

如果有人试图在压力下的系统下记录这一点。(大量内存交换、io、socket、cpu 峰值..)来自 A 的值不会传播到 B。

在 A 中,p 的值似乎为 0。但从 B 的角度来看,p 停留在 1。在我的世界中,当 A 将值设置为 0 时,B 应该检测并将值设置为 0。在实际硬件上,它的工作原理就是这样但在 esxi 上运行时不会。

这似乎在真实硬件和某些虚拟系统下工作正常,但在 vmware 下不行。

我是不是做错了,还是……?

操作系统来宾:win2008服务器

使用 Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01 for x64 编译的代码

主机:esxi 4.1

更新:

对评论的回应:是的,它会在 0 和 1 之间反弹,但正如所写的,线程 B 不会反弹,因为从 p 的值永远/或在 B 的角度发生变化,它会反弹 10-20 次然后停止。

我只想在非常精确的时刻执行 A ( ExecuteAVeryCPUIntesiveThing();) 中的代码块。

生产代码充满了更多的线程和事件、互斥锁和锁,但事实仍然存在,如果我剥离并只使用上面的代码,如果我在来宾操作系统上生成大量 cpu、mm、io,我可以重现它。

4

1 回答 1

2

此代码是等待发生的线程竞赛。您可能从 VMWare 获得它,因为您只为虚拟机分配了一个处理器。

代码缺少的是确保线程 B看到更改值的互锁p。因此,如果线程 A 获得了一个 cpu 内核并继续运行一段时间,而线程 B 被阻塞,等待一个量子运行,那么线程 A 可以p多次设置为 0。线程 B 会忘记这一点,因为它从来没有机会p回到 1。

你需要重新考虑你的锁设计。问题不仅限于 VMWare,它也可能在普通机器上出错,只是可能性要小得多。每月出错一次,给予或接受,无法调试。否则,这是一个经典的生产者/消费者场景,您可以使用线程安全队列来解决这个问题。

于 2012-04-30T13:26:20.453 回答