对于简单的任务,旋转锁应该具有比互斥锁更好的性能。但是,在这个简单的测试中(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
结果显示互斥锁几乎是自旋锁的两倍。旋转锁花费大部分时间在&#34;用户cpu&#34;并且互斥锁花费大部分时间在&#34; sys cpu&#34;。如何实现互斥锁,我应该在这样的简单计算中使用互斥锁而不是自旋锁?任何人都可以解释结果吗?
g ++是4.8.2,OS是Red Hat Enterprise Linux 7.
感谢。
答案 0 :(得分:2)
一些注意事项:
time
实用程序输出中显示的时间是线程使用的CPU时间,而不是实际时间。即使在等待期间,Spinlock也会使用cpu,而内核互斥体会在等待时执行其他进程中的其他线程,而不是为那个CPU时间计费,除了用于实际执行调度的那个(在mutex情况下在sys行中看到的那个) )。
出于同样的原因,上面说的可能是你必须等待从进程开始到结束的总时间在自旋锁的情况下更快,但是你的cpu可能有更高的使用率,这就是你观察到了。
如果您的碰撞机会很低,则线程可能是一个不错的选择,即线程在同步加载中花费的时间与它可以异步执行的加载相比较小。如果你的所有负载都受到互斥锁的保护,那么根本就只需要使用线程的开销 - 你应该将它序列化。
如果您在碰撞时碰撞机会低且等待时间短,则Spinlock很好。在您的情况下,您有8个线程冲突到同一资源,然后要求资源在您释放后立即可用。这意味着平均会有1个线程正在工作,其他7个自旋锁定,将单个线程所需的总CPU时间用于所需时间的8倍(如果你有一个8核机器并且没有其他负载)。在互斥锁情况下,线程暂停并在资源可用时被唤醒,因此没有等待的开销,但是锁定互斥锁需要一些开销,因为在内核中要做的工作是跟踪哪些进程在等待互斥锁,即使它不是太大,你也要进行1.6亿次互斥操作,这总计到你的过程所需的sys
时间