C++ 为什么std::mutex需要很长很不规则的时间来共享?

C++ 为什么std::mutex需要很长很不规则的时间来共享?,c++,pthreads,std,mutex,stdmutex,C++,Pthreads,Std,Mutex,Stdmutex,这段代码演示了互斥锁在两个线程之间共享,但一个线程几乎一直都有互斥锁 #include <thread> #include <mutex> #include <iostream> #include <unistd.h> int main () { std::mutex m; std::thread t ([&] () { while (true) { {

这段代码演示了互斥锁在两个线程之间共享,但一个线程几乎一直都有互斥锁

#include <thread>
#include <mutex>
#include <iostream>

#include <unistd.h>

int main ()
{
    std::mutex m;

    std::thread t ([&] ()
    {
        while (true)
        {
            {
                std::lock_guard <std::mutex> thread_lock (m);

                sleep (1); // or whatever
            }
            std::cerr << "#";
            std::cerr.flush ();
        }
    });

    while (true)
    {
        std::lock_guard <std::mutex> main_lock (m);
        std::cerr << ".";
        std::cerr.flush ();
    }
}
i、 e.
线程锁定
将互斥锁锁定很长时间。几秒钟甚至几十秒钟后,
main_-lock
接收控制(短暂),然后
thread_-lock
将其取回并保留多年。调用
std::this_thread::yield()
不会改变任何事情


为什么这两个互斥锁获得锁的可能性不一样,如何使互斥锁以平衡的方式共享?

std::mutex
的设计并不公平。它不能保证锁的顺序得到遵守,你要么幸运地得到了锁,要么没有

如果你想要更多的公平性,考虑使用<代码> STD::条件语句变量:

#包括
#包括
#包括
#包括
#包括
int main()
{
std::互斥m;
std::条件变量cv;
标准::螺纹t([&]()
{
while(true)
{
std::唯一锁lk(m);

STR::CURR

制作<代码> STD::互斥< <代码>公平有代价。C++中,你不为你不要求的东西付费。

您可以编写一个锁定对象,其中释放锁的一方不能是下一个获得它的一方。更高级的是,您可以编写一个锁定对象,其中只有在其他人正在等待时才会发生这种情况

下面是一个快速、未经测试的公平互斥:

struct fair_mutex {
  void lock() {
    auto l = internal_lock();
    lock(l);
  }
  void unlock() {
    auto l = internal_lock();
    in_use = false;
    if (waiting != 0) {
      loser=std::this_thread::get_id();
    } else {
      loser = {};
    }
    cv.notify_one();
  }
  bool try_lock() {
    auto l = internal_lock();
    if (in_use) return false;
    lock(l);
    return true;
  }
private:
  void lock(std::unique_lock<std::mutex>&l) {
    ++waiting;
    cv.wait( l, [&]{ return !in_use && std::this_thread::get_id() != loser; } );
    in_use = true;
    --waiting;
  }
  std::unique_lock<std::mutex> internal_lock() const {
    return std::unique_lock<std::mutex>(m);
  }
  mutable std::mutex m;
  std::condition_variable cv;
  std::thread::id loser;
  bool in_use = false;
  std::size_t waiting = 0;
};
struct fair\u互斥体{
无效锁(){
自动l=内部锁定();
锁(l);
}
无效解锁(){
自动l=内部锁定();
使用中=错误;
如果(等待!=0){
loser=std::this_线程::get_id();
}否则{
失败者={};
}
cv.通知_one();
}
bool try_lock(){
自动l=内部锁定();
如果(使用中)返回false;
锁(l);
返回true;
}
私人:
无效锁(标准::唯一锁&l){
++等待;
cv.wait(l,[&]{return!in_use&&std::this_thread::get_id()!=loser;});
使用中=正确;
--等待;
}
std::unique_lock internal_lock()常量{
返回标准::唯一锁定(m);
}
可变std::mutexm;
std::条件变量cv;
std::thread::id;
bool in_use=false;
标准::等待的大小=0;
};
这是“公平”的,因为如果你有两个线程争夺一个资源,它们将轮流进行。如果有人在等待锁,任何放弃锁的人都不会再次抓住它

然而,这是线程代码,所以我可能会仔细阅读,但我不相信我第一次尝试写任何东西


您可以将其扩展(成本增加)为n向公平(甚至欧米茄公平)如果有多达N个元素在等待,它们都会在释放线程获得另一个机会之前轮到它们。

互斥不能保证公平性。它们只是防止线程相互干扰。关键部分是不同的:在一种情况下,I/O是关键部分的一部分,而在另一种情况下则不是。@EOF为什么I/O会这样影响事情吗?@spraff——因为I/O通常涉及操作系统在某些硬件操作发生时决定挂起线程。挂起线程时,它不会锁定互斥锁,而其他线程会有更多机会锁定互斥锁。在保持互斥锁时,由您的代码决定要做什么。如果您的代码没有执行rk你想这样做,那由你决定。谢谢,但这只是增加了一个新的谜团。既然两个循环都锁定了同一个互斥锁,为什么我们不会再次遇到同样的问题?@spraff,因为现在我们礼貌地等待,直到另一个线程通知我们可以恢复。(
wait
解锁互斥锁)没有
cv
的方法只是在空闲时获取互斥锁,而不询问或考虑其他线程。在我看来,这似乎是使用条件变量的错误方式。请记住,通知可能会丢失。条件变量必须检查实际条件,即某些变量必须与所需条件匹配。否则,如果通知阳离子在条件唤醒后但在其返回睡眠前到达,通知消失。上述代码具有非UB竞态条件,因为编译器可以自由地将其编译为锁定且不执行任何操作的代码。您必须处理cv中的虚假唤醒,如果忽略非lambda的返回值,请等待我们的代码已损坏。@ZanLynx在这种情况下,它不能丢失,因为
notify
是在临界区内使用的。
#include <thread>
#include <mutex>
#include <iostream>
#include <condition_variable>

#include <unistd.h>

int main ()
{
    std::mutex m;
    std::condition_variable cv;
    std::thread t ([&] ()
    {
        while (true)
        {
            std::unique_lock<std::mutex> lk(m);
            std::cerr << "#";
            std::cerr.flush ();
            cv.notify_one();
            cv.wait(lk);
        }
    });

    while (true)
    {
        std::unique_lock<std::mutex> lk(m);
        std::cerr << ".";
        std::cerr.flush ();
        cv.notify_one();
        cv.wait(lk);
    }
}
struct fair_mutex {
  void lock() {
    auto l = internal_lock();
    lock(l);
  }
  void unlock() {
    auto l = internal_lock();
    in_use = false;
    if (waiting != 0) {
      loser=std::this_thread::get_id();
    } else {
      loser = {};
    }
    cv.notify_one();
  }
  bool try_lock() {
    auto l = internal_lock();
    if (in_use) return false;
    lock(l);
    return true;
  }
private:
  void lock(std::unique_lock<std::mutex>&l) {
    ++waiting;
    cv.wait( l, [&]{ return !in_use && std::this_thread::get_id() != loser; } );
    in_use = true;
    --waiting;
  }
  std::unique_lock<std::mutex> internal_lock() const {
    return std::unique_lock<std::mutex>(m);
  }
  mutable std::mutex m;
  std::condition_variable cv;
  std::thread::id loser;
  bool in_use = false;
  std::size_t waiting = 0;
};