c++:自旋锁或互斥比较(简单计算)

标签 c++ multithreading c++11 mutex spinlock

对于简单的任务,自旋锁应该比互斥锁有更好的性能。然而,在这个简单的测试中(8 个线程递增一个计数器),结果显示不同:

#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <vector>

using namespace std;

class SpinLock {
private:
    atomic_flag lck = ATOMIC_FLAG_INIT;
public:
    void lock() { while(lck.test_and_set(memory_order_acquire)) {} }
    void unlock() { lck.clear(memory_order_release); }
};

int total = 0;

#ifdef SPINLOCK
SpinLock my_lock;
#else
mutex my_lock;
#endif

void foo(int n)
{
    for(int i = 0; i < 10000000; ++i) {
#ifdef SPINLOCK
        lock_guard<SpinLock> lck(my_lock);
#else
        lock_guard<mutex> lck(my_lock);
#endif
        ++total;
    }
}

int main()
{
    vector<thread> v;

    for(int i = 0; i < 8; ++i)
        v.emplace_back(foo, i);

    for(auto& t : v)
        t.join();

    cout << "total: " << total << endl;
    return 0;
}

测试自旋锁:

$ g++ -DSPINLOCK -std=c++11 -Wall -pthread test.cc
$ time ./a.out
total: 80000000
real    0m18.206s
user    2m17.792s
sys     0m0.003s

测试互斥量:

$ g++ -std=c++11 -Wall -pthread test.cc
$ time ./a.out
total: 80000000
real    0m9.483s
user    0m6.451s
sys     1m6.043s

结果显示互斥量几乎比自旋锁快两倍。自旋锁大部分时间都在“用户 cpu”中,而互斥量大部分时间在“系统 cpu”中。互斥量是如何实现的,我应该在像这样的简单计算中使用互斥量而不是自旋锁吗?谁能解释一下结果?

g++ 为 4.8.2,操作系统为 Red Hat Enterprise Linux 7。

谢谢。

最佳答案

一些注意事项:

  1. time 实用程序输出中显示的时间是您的线程正在使用的 CPU 时间,而不是实际时间。自旋锁甚至在等待期间也使用 cpu,而内核互斥锁将在等待时执行其他进程中的其他线程,而不是为该 CPU 时间向您的进程计费,除了用于实际执行调度的线程(您在互斥锁情况下的 sys 行中看到的线程) ).

  2. 出于与上述相同的原因,在自旋锁的情况下,您从进程开始到结束必须等待的总时间可能更快,但是您的 cpu 使用率可能更高,这就是你观察到了。

  3. 如果碰撞几率较低,线程可能是一个不错的选择,也就是说,线程花在同步加载上的时间比它可以异步加载的时间要少。如果您的所有负载都受互斥锁保护,那么使用线程只会产生开销——您可能应该将其序列化。

如果您的碰撞几率和碰撞等待时间都很短,那么自旋锁是很好的选择。在您的情况下,您有 8 个线程冲突到同一资源,然后要求资源在您释放后立即可用。这意味着平均有 1 个线程在工作,其他 7 个自旋锁,使 CPU 总使用时间是单线程所需时间的 8 倍(如果您有一个 8 核机器并且没有其他负载)。在互斥锁的情况下,线程会在资源可用时暂停并唤醒,因此没有等待的开销,但是锁定互斥锁将需要一些开销,因为内核要跟踪哪些进程正在等待互斥锁,即使它不是太大,您也正在执行 1.6 亿次互斥操作,总计 sys 您的进程花费的时间

关于c++:自旋锁或互斥比较(简单计算),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29453377/

相关文章:

c++ - 用C++设计一个状态机

c++ - QML应用程序

c++ - 我应该在我的 C++ WIn32 应用程序中重写 operators new/delete

c++ - 如果返回,lambda 会超出范围吗

c++ - 同时从不同线程写入套接字时会发生什么?

c++ - 如何异步执行从 unique_ptr 到对象的方法?

c++ - std::upper_bound 在 const 成员函数中返回 const 迭代器

c# - 这个方法是线程安全的吗?

c++ - 确定代码是否在特定线程中运行

c++ - C++/gcc/linux 中的 Continuations/Coroutines/Generators