释放锁时的线程调度

Thread scheduling when releasing lock

我在解锁互斥量时的期望是调度程序会检查当前尝试锁定此互斥量的其他线程,然后执行其中一个等待线程。我写了一个测试程序(见下面的代码),有 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 上,std::mutex 是使用超薄 reader/writer 锁实现的。这个锁实现是不公平的(意味着它不保证等待线程获取锁的顺序)。 Windows 前段时间不再使用公平锁,因为 Microsoft 认为锁护卫是比线程饥饿更严重的问题。

您可以在 Microsoft 文档上阅读有关纤薄 reader/writer 锁的更多信息:Slim Reader/Writer (SRW) Locks

Joe Duffy 也发表了关于公平与锁定车队问题的博文:Anti-convoy locks in Windows Server 2003 SP1 and Windows Vista