自旋锁,不进行阻塞,空耗CPU资源。
在针对多核CPU、资源占用时间不长的情况下很高效。
要求:锁住的资源足够小,计算时间足够短。
优点:相对于互斥锁,不会使我们的线程陷入内核态,效率高
缺点:如果资源每个线程对资源占用时间比较长,那么我们自旋操作将会大量的空耗CPU,效率低下。同时,对于单核CPU来说,自旋锁在自旋时进行大量空耗等待对方线程释放锁,然而单核CPU一次只能运行一个线程,因此,等待当前线程时间片到达切换之后,该自旋锁才会释放。
#include <iostream>
#include <atomic>
#include <thread>
using namespace std;
class CAS // 自旋锁
{
private:
std::atomic<bool> flag; // true 加锁、false 无锁
public:
// 注意这里的初值,初值为false。则第一 lock() 不锁,第二个进程 lock时会锁住。知道另一个进程unlock()
// 如果初值为 true,那么第一调用lock(),就会阻塞。知道调用unlock().
CAS(int _flag = false) :flag(_flag) {}
~CAS() {}
CAS(const CAS&) = delete;
CAS& operator=(const CAS&) = delete;
void lock() // 加锁
{
bool expect = false; // 原子地比较
/* expected, desired,std::memory_order success,
std::memory_order failure
参数会要求在这里传入期待的数值和新的数值。它们对比变量的值和期待的值是否一致,
- 如果是,则替换为用户指定的一个新的数值。
- 如果不是,则将变量的值和期待的值交换。
如果 *this == expected 则 *this = desired ,返回true
否则 *this != expected 则 expected = *this,返回false
*/
while (!flag.compare_exchange_strong(expect, true))
{
/*
加锁操作:当前为假,才能加锁
如果 flag 为false ,则 flag == expect ,falg 被设置为 true。 同时语句返回假,退出。
如果 flag 为true , 即 flag != expect, expect被修改为 true。 然而循环体内又把 expect 修改为false,就会一直循环的执行,直至加锁。
加锁成功的条件是,执行了 unlock() ,将falg设置为false
*/
expect = false;
}
}
void unlock()
{ // 原子地赋值/写操作
// 原子地以非原子对象替换原子对象的值
flag.store(false);
}
};
int num = 0;
CAS sm;
void fun()
{
for (int i = 0; i < 100000; ++i)
{
sm.lock(); // 锁资源的消耗代价非常小,无需进行内核态与用户态的转态转换(线程切换),来锁资源
++num;
sm.unlock();
}
}
int main()
{
thread t1(fun), t2(fun);
t1.join();
t2.join();
cout << num << endl; // 理论上的值为 2000
return 0;
}
2021/8/27更新…
#include <iostream>
#include <atomic>
#include <thread>
using namespace std;
class CAS // 自旋锁
{
private:
std::atomic<bool> flag; // true 加锁、false 无锁
public:
CAS(int _flag = false) :flag(_flag) {}
~CAS() {}
CAS(const CAS&) = delete;
CAS& operator=(const CAS&) = delete;
void lock() // 加锁
{
bool expect = false;
while (!flag.compare_exchange_strong(expect, true))
{
expect = false;
}
}
void unlock() { flag.store(false); }
};
class Foo {
CAS cas_1{ true }; // 就地初始化,等号,或花括号
CAS cas_2{ true };
public:
void first() {
cout << "First" << endl;
cas_1.unlock(); // 解锁
}
void second() {
cas_1.lock();
cout << "Second" << endl;
cas_2.unlock();
}
void third() {
cas_2.lock();
cout << "Third" << endl;
}
};
int main()
{
Foo foo;
// 线程执行顺序 First、Second、Third
thread t1(&Foo::first, &foo);
thread t2(&Foo::second, &foo);
thread t3(&Foo::third, &foo);
t1.join();
t2.join();
t3.join();
return 0;
}