在单独的线程C ++上解锁时,互斥锁不会解锁

时间:2017-03-07 19:12:30

标签: c++ multithreading mutex

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

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

#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之间交替吗?应该在每次循环迭代结束时释放锁,这应该允许另一个线程控制互斥锁。为什么不发生这种情况?有什么办法可以实现吗?

3 个答案:

答案 0 :(得分:3)

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

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

        std::cout << printout << std::endl;
        count++;
    } // lock is released here
    // ...
}

答案 1 :(得分:2)

  

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

没有这样的保证。

  

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

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

  

有没有办法让它成为现实?

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

答案 2 :(得分:0)

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

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

  

当控件离开创建lock_guard对象的作用域时,将破坏lock_guard并释放互斥锁。   lock_guard类是不可复制的。

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

enter image description here

如果你想锁定所有的threadFunctor,你应该暂时取出lock_guard,如下所示:

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