使用std :: lock的大量CPU负载(c ++ 11)

时间:2012-12-02 08:59:33

标签: c++ c++11 mutex stdthread

我最近实现线程/互斥管理器的努力最终导致75%的CPU负载(4核心),而所有四个正在运行的线程都处于睡眠状态或等待互斥锁解锁。

特定的类太大了,不能完全发布在这里,但我可以将原因缩小到两个互斥锁的死锁安全获取

std::unique_lock<std::mutex> lock1( mutex1, std::defer_lock );
std::unique_lock<std::mutex> lock2( mutex2, std::defer_lock );
std::lock( lock1, lock2 );

该课程的另一部分在std::condition_variable上使用wait() notify_one()mutex1,以便有选择地同时执行某些代码。

简单的改为

std::unique_lock<std::mutex> lock1( mutex1 );
std::unique_lock<std::mutex> lock2( mutex2 );

将CPU使用率降至正常的1-2%。

我不相信,std::lock()函数效率低下。这可能是g ++ 4.6.3中的错误吗?

编辑:(示例)

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

std::mutex mutex1, mutex2;
std::condition_variable cond_var;

bool cond = false;
std::atomic<bool>done{false};

using namespace std::chrono_literals;

void Take_Locks()
    {
    while( !done )
        {
        std::this_thread::sleep_for( 1s );

        std::unique_lock<std::mutex> lock1( mutex1, std::defer_lock );
        std::unique_lock<std::mutex> lock2( mutex2, std::defer_lock );
        std::lock( lock1, lock2 );

        std::this_thread::sleep_for( 1s );
        lock1.unlock();
        lock2.unlock();
        }
    }

void Conditional_Code()
    {
    std::unique_lock<std::mutex> lock1( mutex1, std::defer_lock );
    std::unique_lock<std::mutex> lock2( mutex2, std::defer_lock );

    std::lock( lock1, lock2 );
    std::cout << "t4: waiting \n";

    while( !cond )
        cond_var.wait( lock1 );

    std::cout << "t4: condition met \n";
    }

int main()
    {
    std::thread t1( Take_Locks ), t2( Take_Locks ), t3( Take_Locks );
    std::thread t4( Conditional_Code );

    std::cout << "threads started \n";
    std::this_thread::sleep_for( 10s );

    std::unique_lock<std::mutex> lock1( mutex1 );
    std::cout << "mutex1 locked \n" ;
    std::this_thread::sleep_for( 5s );

    std::cout << "setting condition/notify \n";
    cond = true;
    cond_var.notify_one();
    std::this_thread::sleep_for( 5s );

    lock1.unlock();
    std::cout << "mutex1 unlocked \n";
    std::this_thread::sleep_for( 6s );

    done = true;
    t4.join(); t3.join(); t2.join(); t1.join();
    }

4 个答案:

答案 0 :(得分:25)

在我的机器上,以下代码每秒打印10次并消耗几乎0 cpu,因为大多数情况下线程在锁定的互斥锁上处于休眠或阻塞状态:

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

using namespace std::chrono_literals;

std::mutex m1;
std::mutex m2;

void
f1()
{
    while (true)
    {
        std::unique_lock<std::mutex> l1(m1, std::defer_lock);
        std::unique_lock<std::mutex> l2(m2, std::defer_lock);
        std::lock(l1, l2);
        std::cout << "f1 has the two locks\n";
        std::this_thread::sleep_for(100ms);
    }
}

void
f2()
{
    while (true)
    {
        std::unique_lock<std::mutex> l2(m2, std::defer_lock);
        std::unique_lock<std::mutex> l1(m1, std::defer_lock);
        std::lock(l2, l1);
        std::cout << "f2 has the two locks\n";
        std::this_thread::sleep_for(100ms);
    }
}

int main()
{
    std::thread t1(f1);
    std::thread t2(f2);
    t1.join();
    t2.join();
}

示例输出:

f1 has the two locks
f2 has the two locks
f1 has the two locks
...

我在OS X上运行它,而Activity Monitor应用程序说这个过程使用0.1%cpu。该机器是Intel Core i5(4核心)。

我很乐意以任何方式调整此实验,以尝试创建实时锁定或过多的CPU使用。

<强>更新

如果此程序在您的平台上使用了过多的CPU,请尝试将其更改为调用::lock(),而不是在其中定义:

template <class L0, class L1>
void
lock(L0& l0, L1& l1)
{
    while (true)
    {
        {
            std::unique_lock<L0> u0(l0);
            if (l1.try_lock())
            {
                u0.release();
                break;
            }
        }
        std::this_thread::yield();
        {
            std::unique_lock<L1> u1(l1);
            if (l0.try_lock())
            {
                u1.release();
                break;
            }
        }
        std::this_thread::yield();
    }
}

我很想知道这对你有什么影响,谢谢。

更新2

经过长时间的拖延,我写了一篇关于这个主题的论文初稿。本文比较了完成这项工作的4种不同方式。它包含可以复制并粘贴到您自己的代码中并自行测试的软件(请报告您找到的内容!):

http://howardhinnant.github.io/dining_philosophers.html

答案 1 :(得分:6)

std::lock()非成员函数可能导致实时锁定问题或性能下降,它只保证“永不死锁”。

如果您可以按设计确定多个互斥锁的“锁定顺序(锁定层次结构)”,则最好不要使用通用std::lock(),而是以预先确定的顺序锁定每个互斥锁。

有关详细信息,请参阅Acquiring Multiple Locks Without Deadlock

答案 2 :(得分:5)

正如文档所说, [t]对象被一系列未指定的锁定,try_lock,unlock 调用锁定。如果互斥体被其他线程持有很长一段时间,那么根本没有办法可能有效。没有旋转,这个功能无法等待。

答案 3 :(得分:0)

首先,我要感谢所有答案。

在处理示例代码的过程中,再现了这种效果,我找到了麻烦的来源。

条件部分锁定两个互斥锁,而它只使用一个std::condition_variable::wait()函数。

但我仍然想知道,幕后发生了什么,会产生如此高的CPU负载。