【发布时间】:2018-11-22 16:14:35
【问题描述】:
我在解锁互斥锁时的期望是调度程序检查当前尝试锁定该互斥锁的其他线程,然后执行其中一个等待线程。我编写了一个测试程序(见下面的代码),其中有 2 个线程都试图在一个循环中获取相同的互斥锁并做一些工作(睡眠 1 毫秒)。不同之处在于,一个线程 t1 在解锁和尝试重新获取互斥锁之间等待片刻,而另一个线程 t2 则不会。我期待两个线程获取互斥锁的次数大致相同。但是,在 Windows 上,t1 通常只获取一次互斥锁,而其他线程则获取数百次。在 linux 上,行为是不同的,两个线程都使用 t2 大约两倍的数量完成工作。为什么 Windows 上的 t1 几乎从不获取互斥锁?我该如何修改代码才能做到这一点?
示例代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
using namespace std;
int main()
{
mutex m;
atomic<bool> go(false);
int t1Counter = 0, t2Counter = 0;
thread t1([&] {
while(!go);
while(go) {
this_thread::sleep_for(100us);
lock_guard<mutex> lg(m);
this_thread::sleep_for(1ms);
++t1Counter;
}
});
thread t2([&] {
while(!go);
while(go) {
lock_guard<mutex> lg(m);
this_thread::sleep_for(1ms);
++t2Counter;
}
});
go = true;
this_thread::sleep_for(1s);
go = false;
t1.join();
t2.join();
cout << t1Counter << " " << t2Counter << endl;
}
【问题讨论】:
-
您正在寻找的搜索词是“公平”和“饥饿”。 Windows 互斥锁不保证公平。我不熟悉Linux;看起来它的实现更公平。
-
在解锁和重新锁定之间调用
std::this_thread::yield可能会稍微改变画面。 -
@IgorTandetnik 变化不大。线程 t1 运行了大约 5 次而不是只运行一次,但 t2 仍然运行了数百次。
-
意外同步问题,那些 sleep_for() 调用不是无辜的。您可以从 t2Counter 值中看出一些问题。如果 t2 运行(几乎)畅通无阻,那么该值应该接近 1000。我的水晶球说你看到的东西接近 60 或 95。在 Windows 上,睡眠只能在时钟滴答中断时完成,它每滴答 64 次默认为第二个。
-
@HansPassant 你是对的。用一些忙碌的等待睡眠替换 sleep_for 确实会产生更明智的结果。
标签: c++ multithreading mutex