多线程环境下使用竞态资源时要使用锁来保证线程安全的实现。锁的实现有三种,互斥锁,自旋锁和原子管理,
互斥锁的原理是当线程A加锁后,线程B尝试加锁发现被占用,线程B由唤醒状态切换为睡眠状态,当线程A解锁后,会通知内核,内核再通知线程B,线程B再由睡眠状态切换为唤醒状态。
互斥锁代码如下:
#include <iostream>
#include <mutex>
#include <memory>
#include <thread>class testClass {
public:void showNum(){std::lock_guard<std::mutex> lock(_mutex);std::cout << "当前数字是" << num << std::endl;}void NumIncrease(){std::lock_guard<std::mutex> lock(_mutex);num++;}private:int num = 0;std::mutex _mutex;
};void increaseNum(std::shared_ptr<testClass> test)
{for (int i = 0; i < 1000; i++){test->NumIncrease();}test->showNum();
}int main()
{std::shared_ptr<testClass> test = std::make_shared<testClass>();std::thread t0(increaseNum, test);std::thread t1(&increaseNum, test);std::thread t2(&increaseNum, test);std::thread t3(&increaseNum, test);std::thread t4(&increaseNum, test);std::thread t5(&increaseNum, test);std::thread t6(&increaseNum, test);std::thread t7(&increaseNum, test);std::thread t8(&increaseNum, test);std::thread t9(&increaseNum, test);t0.join();t1.join();t2.join();t3.join();t4.join();t5.join();t6.join();t7.join();t8.join();t9.join();return 0;
}
程序输出如下:
注释掉锁之后
程序输出如下
下面介绍自旋锁:自旋锁没有直接调用的库,需要使用atomic手动实现
自旋锁代码如下:
#include <iostream>
#include <mutex>
#include <memory>
#include <thread>
#include <atomic>//自旋锁类class Spinlock {
public:// 尝试获取锁void lock() {// 使用原子操作来设置锁标志,如果失败则自旋等待while (lock_flag.test_and_set(std::memory_order_acquire)) {}// 锁已被当前线程获取}// 释放锁void unlock() {lock_flag.clear(std::memory_order_release);}private:std::atomic_flag lock_flag = ATOMIC_FLAG_INIT; //原子标志表示锁
};class testClass {
public:void showNum(){std::lock_guard<Spinlock> lock(_mutex);std::cout << "当前数字是" << num << std::endl;}void NumIncrease(){std::lock_guard<Spinlock> lock(_mutex);num++;}private:int num = 0;Spinlock _mutex;
};void increaseNum(std::shared_ptr<testClass> test)
{for (int i = 0; i < 1000; i++){test->NumIncrease();}test->showNum();
}int main()
{std::shared_ptr<testClass> test = std::make_shared<testClass>();std::thread t0(increaseNum, test);std::thread t1(&increaseNum, test);std::thread t2(&increaseNum, test);std::thread t3(&increaseNum, test);std::thread t4(&increaseNum, test);std::thread t5(&increaseNum, test);std::thread t6(&increaseNum, test);std::thread t7(&increaseNum, test);std::thread t8(&increaseNum, test);std::thread t9(&increaseNum, test);t0.join();t1.join();t2.join();t3.join();t4.join();t5.join();t6.join();t7.join();t8.join();t9.join();return 0;
}
输出如下:
输出结果表明可以实现锁的功能,具体实现通过原子变量atomitic实现,代码中的std::memory_order_acquire功能理解的时候参考了下面UP主的视频。通俗来说就是保证汇编时编译器优化打乱的顺序,再标记点之后线程安全。
【量子速读 C++ Concurrency in Action 第五章 Memory Order】 https://www.bilibili.com/video/BV1br4y197in/
递归锁(可重入锁)代码如下:
//定义一个递归锁,在函数中使用递归锁保证线程安全,创建多个线程调用函数。#include <iostream>
#include <thread>
#include <mutex>
#include <vector>//定义一个递归锁
std::recursive_mutex recursiveMutex;
//std::mutex _mutex;void recursiveFunction(int count)
{std::lock_guard<std::recursive_mutex> lock(recursiveMutex);//std::lock_guard<std::mutex> lock(_mutex);std::cout << "当前线程是" << std::this_thread::get_id() << ",计数是" << count << std::endl;if (count > 0){//递归调用自身recursiveFunction(count-1);}
}int main()
{int thread_num = 5;int count = 6;//创建线程向量std::vector<std::thread> threadVec;for (int i = 0; i < thread_num; i++){threadVec.emplace_back(recursiveFunction, count);}for (auto& it : threadVec){it.join();}return 0;
}
程序输出如下
代码中将递归锁改为互斥锁后,作为对比
程序输出如下