各种锁机制
1.互斥锁
1.std::mutex mutex mtx mtx.lock() mtx.unlock() 2.std::lock_guard 利用RAII技术进行上锁和解锁 声明 template<class Mutex> class lock_guard; 构造函数 explicit lock_guard(mutex_type& m); 3.互斥锁底层实现 //1:上锁 0:解锁 bool testAndSet(bool& mutex){ bool value=mutex; mutex=true; return value; } while(testAndSet(mutex)) ; //如果上锁后有线程去获得锁,则一直while循环 / 临界区 / mutex=0; //解锁
2.自旋锁(独占锁)
1.实现 class SpinLock { public: void lock() { while (flag.test_and_set(memory_order_acquire)); //当一个线程无法获取锁时一直阻塞在while循环 } void unlock() { flag.clear(memory_order_release); } private: std::atomic_flag flag; }; 2.应用场景 当线程无法获取锁时一直试图去获取锁(忙等待),而不是进入睡眠态, 适用于线程使用锁时间较短的场景(当线程切换开销比较大时)。
3.读写锁
1.实现 利用互斥锁实现读写锁 class RWLOCK{ public: RWLOCK():flag(0){} void readLock(){ mtx.lock(); while(flag<0){ cond.wait(mtx); } ++flag; mtx.unlock(); } void readUnlock(){ mtx.lock(); //最后一个读锁解锁,可以进行写锁上锁 if(--stat==0){ cond.notify_all(); } } void writeLock(){ mtx.lock(); //只有不上锁时才能上写锁 while(flag!=0){ cond.wait(mtx); } flag=-1; mtx.unlock(); } void writeUnlock(){ mtx.lock(); flag = 0; cond.notify_all(); // 叫醒所有等待的读和写操作 mtx.unlock(); } private: atomic<int> flag; //0:无锁 >0:读锁的个数 <0:写锁 condition_variable cond; mutex mtx; };
4.条件变量
1.常用方法 std::mutex mtx; std::condition_variable cond; cond.wait(mtx); cond.notify_all(); 2.为何条件变量要搭配锁使用? //消费者线程 while(queue.empty()){ / 1. ... / cond.wait(mtx); } //生产者线程 cond.notify_all() 假设有一个生产者线程和一个消费者线程,消费者此时进入1处, 而此时生产者线程发送唤醒信号,而消费者线程未进入wait态, 就错过了该唤醒状态,此时这个线程就永远阻塞了。 而使用锁,进入wait时就先上锁,然后把线程放入等待队列,然后释放锁,就不会错过唤醒信号。 等待队列的线程竞争锁,谁获取锁谁被唤醒,获取资源, 因此,要用while循环,未被唤醒的线程继续等待下一次被唤醒。