【问题标题】:Mutex not unblocking when unlocked on separate thread C++在单独的线程 C++ 上解锁时互斥锁不会解除阻塞
【发布时间】:2017-03-07 19:12:29
【问题描述】:

我试图理解为什么我的互斥锁的行为不像我预期的那样。

我正在调试另一个问题,并决定制作一个非常简单的可执行文件来直接测试互斥体的行为。这是我想出的:

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

int main(int argc, char** argv)
{
        std::mutex myMutex;

        auto threadGenerator = [&] (std::string printout)
        {
                auto threadFunctor = [&, printout] {

                        int count = 0;
                        while (count < 300)
                        {
                                std::lock_guard<std::mutex> lock(myMutex);


                                std::cout << printout << std::endl;
                                count++;

                                // Sleep ensures that the other thread will be waiting on mutex
                                // when I release lock
                                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                        }
                };

                return threadFunctor;
        };

        auto thread1Functor = threadGenerator("Thread 1 got lock");
        auto thread2Functor = threadGenerator("Thread 2 got lock");

        std::thread thread1(thread1Functor);
        std::thread thread2(thread2Functor);

        thread1.join();
        thread2.join();

        return 0;
}

这只是产生了两个线程,它们重复锁定和解锁互斥体,同时打印一些输出。我添加了 sleep 来强制 lock_guard 阻塞并且线程互相等待。

这会产生以下输出:

Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock
Thread 1 got lock

最终,一旦线程 1 完成,线程 2 将再次开始获取锁。

他们不应该在线程 1 和线程 2 之间交替吗?锁应该在每次循环迭代结束时释放,这应该允许其他线程控制互斥锁。为什么没有发生这种情况?有什么方法可以实现吗?

【问题讨论】:

  • 是的,这会解决它,但有必要这样做吗?每当您使用互斥体时,是否需要添加睡眠周期才能使其工作?
  • @user2445507 不,没有必要
  • std::mutex 不保证公平。
  • 显然这是用于测试概念的玩具代码,但如果您想要线程 A,然后 B 并返回 A 等......并且它们从不同时运行,您可能根本不需要线程。你想要一个状态机。
  • 我想我需要一个条件变量才能真正让它按预期工作。但这无论如何都是一个人为的例子。现在我知道 mutex 并不能保证公平,所以我会为此做好计划。

标签: c++ multithreading mutex


【解决方案1】:

他们不应该在线程 1 和线程 2 之间交替吗?

不,没有这样的保证。

锁应该在每次循环迭代结束时释放,这应该允许其他线程控制互斥锁。为什么没有发生这种情况?

因为您的第一个线程锁定互斥锁,休眠,解锁互斥锁,然后尝试再次锁定互斥锁。现在线程 1 和线程 2 都在尝试获取 mutex,但是线程 1 处于运行状态,而线程 2 处于休眠状态,因此线程 1 更有可能先获取 mutex。

有什么办法可以实现吗?

你的程序不应该区分线程并且不应该依赖于顺序。在实际情况下,多个线程等待互斥锁获取数据,一个线程将其放在那里,因此所有等待线程都处于等待状态,因此它们获得互斥锁的概率相似。但这可能特定于特定的硬件、操作系统和版本。你的程序不应该依赖于哪个特定线程获得了互斥锁。

【讨论】:

    【解决方案2】:

    将互斥锁锁定时的代码放在单独的范围内:

    while (count < 300) {
        {
            std::lock_guard<std::mutex> lock(myMutex);
    
            std::cout << printout << std::endl;
            count++;
        } // lock is released here
        // ...
    }
    

    【讨论】:

    • 只有在“// ...”中保持睡眠才能解决问题,我不想这样做,因为我会在代码中有很多睡眠。
    • 我不想要睡眠,因为它会减慢我的程序。至少,我想知道我可以添加的最小睡眠是多少,以保证它会起作用。
    • @user 检查std::this_thread::yield() 释放锁定后是否适用于您的情况。
    • 这似乎确实有所改善。我仍然从同一个线程中得到一长串的打印输出。不是所有 300 个都在一起,而是 30-40 个链。不过,我仍然更喜欢没有链子
    • @user2445507 你在错误的树上吠叫。你的模拟不正确,所以你不需要让它工作,你需要创建正确的模拟。
    【解决方案3】:

    正如@πάνταῥεῖ 所说,每次线程进入 While 循环时,范围都由 While 部分分隔。因此,std::lock_guard&lt;std::mutex&gt; 会在每次进入 while 的代码完成时被销毁,以此类推,每次线程再次执行 while 部分时都会创建一个新的作用域。

    根据 CppReference http://en.cppreference.com/w/cpp/thread/lock_guard:

    当控制离开创建 lock_guard 对象的范围时, lock_guard 被破坏并且互斥体被释放。 lock_guard 类是不可复制的。

    这就是你有这样的行为的原因:

    如果你想锁定所有的threadFunctor你应该把lock_guard从while中取出来,像这样:

    std::mutex myMutex;
    
    auto threadGenerator = [&](std::string printout)
    {
        auto threadFunctor = [&, printout] {
            //Scope of threadFunctor
            int count = 0;
            std::lock_guard<std::mutex> lock(myMutex); //The Lock is working into the Scope of threadFunctor
    
            while (count < 100)
            {
                //New Scope is create (While's Scope)
                std::cout << printout << std::endl;
                count++;
                // Sleep ensures that the other thread will be waiting on mutex
                // when I release lock
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
    
            //At this point the Lock will be released.
        };
    
        return threadFunctor;
    };
    

    【讨论】:

      猜你喜欢
      • 2012-12-25
      • 2022-07-31
      • 1970-01-01
      • 2011-07-24
      • 2018-05-23
      • 2010-11-22
      • 1970-01-01
      相关资源
      最近更新 更多