简体   繁体   English

为什么std :: mutex需要花费很长时间,非常不规律的时间来共享?

[英]Why is std::mutex taking a long, highly irregular amount of time to be shared?

This code demonstrates that the mutex is being shared between two threads, but one thread has it nearly all of the time. 此代码演示了两个线程之间共享互斥锁,但几乎所有时间都有一个线程。

#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 ();
    }
}

Compiled with g++ 7.3.0 on Ubuntu 18.04 4.15.0-23-generic. 在Ubuntu 18.04 4.15.0-23-generic上用g ++ 7.3.0编译。

The output is a mix of both # and . 输出是#和的混合. characters, showing that the mutex is being shared, but the pattern is surprising. 字符,显示共享互斥锁,但模式令人惊讶。 Typically something like this: 通常是这样的:

.......#####..........................##################......................##

ie the thread_lock locks the mutex for a very long time. thread_lock锁定互斥锁长一段时间。 After several or even tens of seconds, the main_lock receives control (briefly) then the thread_lock gets it back and keeps it for ages. 几秒甚至几十秒后, main_lock接收控制(简要地)然后thread_lock将其恢复并保持多年。 Calling std::this_thread::yield() doesn't change anything. 调用std::this_thread::yield()不会改变任何东西。

Why are the two mutexes not equally likely to gain the lock, and how can I make the mutex be shared in a balanced fashion? 为什么两个互斥锁不太可能获得锁定,如何以均衡的方式共享互斥锁?

std::mutex isn't designed to be fair. std::mutex的设计并不公平。 It doesn't guarantee that the order of locking is kept, you're either lucky to get the lock or not. 它不能保证锁定的顺序是保持的,你要么幸运得到锁定。

If you want more fairness, consider using a std::condition_variable like so : 如果你想要更公平,可以考虑像这样使用std::condition_variable

#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);
    }
}

Making std::mutex fair would have a cost. 制作std::mutex会产生费用。 And in C++ you don't pay for what you don't ask for. 在C ++中,你不需要支付你不需要的东西。

You could write a locking object where the party releasing the lock cannot be the next one to get it. 你可以编写一个锁定对象,释放锁的一方不能成为下一个锁定对象。 More advanced, you could write one where this only occurs if someone else is waiting. 更高级,您可以写一个只有在其他人等待时才会出现这种情况。

Here is a quick, untested stab at a fair mutex: 这是一个快速的,未经测试的公平互斥的刺:

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;
};

it is "fair" in that if you have two threads contending over a resource, they will take turns. 它是“公平的”,如果你有两个线程争夺资源,他们将轮流。 If someone is waiting on a lock, anyone giving up the lock won't grab it again. 如果有人在等锁,任何放弃锁定的人都不会再抓住它。

This is, however, threading code. 但是,这是线程代码。 So I might read it over, but I wouldn't trust my first attempt to write anything. 所以我可能会读完它,但我不相信我第一次尝试写任何东西。

You could extend this (at increasing cost) to be n-way fair (or even omega-fair) where if there are up to N elements waiting, they all get their turn before the releasing thread gets another chance. 您可以将此(以不断增加的成本)扩展为n-way fair(甚至是omega-fair),如果有多达N个元素在等待,它们都会在释放线程获得另一个机会之前轮到他们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM