文章目录
(一) 线程概念、创建及传参
(二) 独占互斥锁–mutex,lock_guardy与其他mutex
(三) unique_lock替换lock_guardy
(四) 单例模式(Singleton)下的线程安全问题
(五) window临界区
(六) condition_variable条件变量
(七) std::async异步任务与std::future< >
(八) packaged_task< >与promise< >
(九) 原子操作atomic<>简介
(1)单例模式
- 单例 Singleton 是设计模式的一种,其特点是只提供唯一一个类的实例,具有全局变量的特点,在任何位置都可以通过接口获取到那个唯一实例
- 一般用于操作数据池,或配置属性封装成类
- 外部不可创建,创建后指向指针也不可释放
(2)多线程获取单例模式接口
1.若多个线程同时接口,则可能在单例初始化时多个线程同时创建了多个单例,导致内存泄漏
2.解决以上问题必须对其部分上锁,也需要考虑效率问题
3.双重判定解决(减少线程的堵塞等待)
4.std::call_once()类模板解决
5.call_once需要配合once_flag使用
- call_once的使用
1.首先定义一个std::once_flag flag标记,类似于锁
2.创建初始化单例构造的成员函数
3.在接口函数调用std::call_once()
4.第一个参数传入flag,第二个参数传入函数名
std::once_flag g_flag;
void CreatSingleton() //创建单例
{
if (m_instance == nullptr)
{
m_instance = new CSingleton();
static AutoRelease autorelease; //自动析构类
}
}
CSingleton* CSingleton::GetInstance()
{
std::call_once(g_flag, CreatSingleton); //多个线程进入则等待,一个线程创建,相当于std::once_flag g_flag的锁,创建后其他线程退出,后面不会再调用
return m_instance;
}
- 源码如下
#include <iostream>
#include<thread>
#include<mutex>
using namespace std;
class CSingleton
{
public:
static CSingleton* GetInstance();
void run()
{
m_mutexdata.lock();
m_count++;
m_mutexdata.unlock();
cout << "run()执行" << m_count << endl;
}
private:
CSingleton(); //私有构造函数防止被外部创建
~CSingleton(); //私有析构函数防止外部使用 delete
class AutoRelease //自动析构类
{
public:
AutoRelease() {};
~AutoRelease() {
delete m_instance;
cout << "释放单例~" << endl;
m_instance = nullptr;
};
private:
};
private:
static CSingleton* m_instance;
static std::mutex* m_mutex;
int m_count = 0;
mutex m_mutexdata; //保护data
};
CSingleton* CSingleton::m_instance = nullptr; //必须初始化
mutex* CSingleton::m_mutex = new mutex(); //必须初始化
CSingleton::CSingleton()
{
cout << "CSingleton()创建" << endl;
}
CSingleton::~CSingleton()
{
cout << "CSingleton()销毁" << endl;
}
CSingleton* CSingleton::GetInstance()
{
//双检查锁,但由于内存读写reorder(优化)仍然不安全
//由于编译成汇编时的优化,导致分配地址与构造顺序不明确(不仅C++)
//若先分配地址而为构造,其他线程在在此时获取则会返回一个中间状态对象,造成未知错误
if (m_instance == nullptr) //双重判定 提高效率(线程堵塞等待)
{
std::unique_lock<mutex> mylock(*m_mutex);//若没有这行则创建多个,测试最终data不为300
if (m_instance == nullptr) //若多个线程同时访问则会创建多个导致内存泄漏
{
m_instance = new CSingleton();
static AutoRelease autorelease;
}
}
//以上可用 void CreatSingleton() 完成new创建
//std::call_once(g_flag, CreatSingleton); //多个线程进入则等待,一个线程创建 相当于std::once_flag g_flag的锁
return m_instance;
}
void getSingleton()
{
for (int i = 0; i < 100; i++)
{
CSingleton* p = CSingleton::GetInstance();
p->run();
}
}
int main()
{
//CSingleton* p1 = CSingleton::GetInstance();
//p1->run();
delete p1; //无法通过编译
//CSingleton* p2 = CSingleton::GetInstance();
//p2->run();
thread mythread1(getSingleton);
thread mythread2(getSingleton);
thread mythread3(getSingleton);
mythread1.join();
mythread2.join();
mythread3.join();
}
- 解决双重判定reorder问题
非跨平台使用volatile关键字,C++11后使用atomic类模板解决
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;
Singleton* Singleton::getInstance() {
Singleton* tmp = m_instance.load(std::memory_order_relaxed);
//确保内存不会被reorder优化
std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
//配套使用
std::atomic_thread_fence(std::memory_order_release);//释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}