目录
(二) 独占互斥锁–mutex,lock_guardy与其他mutex
(三) unique_lock替换lock_guardy
(四) 单例模式(Singleton)下的线程安全问题
(五) window临界区
(六) condition_variable
(七) std::async异步任务与std::future< >
(八) packaged_task< >与promise< >
(九) 原子操作atomic<>简介
(1)基本概念
- 并发:一个程序同时执行多个程序任务。
- 进程:可执行程序运行,便创建了一个进程。
- 线程:代码的执行通路,每个进程都有一个主线程,且唯一,与进程一起产生、结束,不是越多越好,每个线程需要一个独立的堆栈空间,线程之间切换需切要保存很多中间的状态,切换过多降低程序的运行时间,最好不要超过两百个。
(2)创建线程
1.使用thread创建线程
2.创建后使用join,即与主线程汇合
3.创建后使用detacn,即与主线程分离,可能引发内存问题
4.joinable 是否可join,返回布尔值
5.类创建线程 (重载操作符)
6.lamda表达式创建线程
join()函数,让主线程等待子线程执行完成,相当于阻塞,join执行完毕,join()后面的主线程才会继续执行。这样可以有效防止主程序已经停止运行,而子程序没有运行结束,子程序不得不中断。
detach()函数,让子线程和主线程分开独自执行,无法控制,该子线程由c++运行时库进行控制管理,运行结束也是由c++运行时库进行释放。但是当主线程结束,该子线程也会随即中断结束。
详情见代码块1
(3)线程传参
1.使用detach 传入 char* 有可能主函数执行完内存释放了产生异常,且必需加const
2.解决以上方法可以 使用构造临时对象 string("") 传入,若传入值则在线程内产生构造函数,但主程序已经结束释放
3.线程调用的函数参数需要加上 & 否则会调用三次构造
4.以上调用会产生构造函数与拷贝构造
5.std::this_thread::get_id() 可获取当前进程id
6.使用 mutable 定义 可修改const变量内容
7.std::ref() 可传入引用 不产生拷贝构造 也可使用&
8.调用类内部函数及传参
9.类似智能指针传引用需使用 std::move()
注意:在使用detach函数时,注意子线程和主线程之间不要使用“引用”方式来传递参数,否则某一个先结束,释放变量,会导致另一个线程出现bug。
详情见代码块2
- 代码块1
#include <iostream>
#include<thread>
using namespace std;
//子线程调用函数
void myprint();
class CPrint
{
public:
CPrint() {};
~CPrint() {};
void operator()();
};
void test1()//类创建线程
{
CPrint my;
thread myobj(my);
myobj.join();
}
void test2() //使用lambda表达式创建线程
{
auto my = [] {
for (auto i = 0; i < 10; i++)
{
cout << "mylambda:[" << i << "]" << endl;
}
};
thread myobj(my);
myobj.detach();
}
int main()
{
//创建子线程,并开始执行函数
thread myobj(myprint);
//汇合,即堵塞
//主线程等待子线程运行结束后才继续运行主线程,没join主线程提前结束会发生异常
//if (myobj.joinable())
// myobj.join();
//分离,非堵塞
//主线程结束了仍执行,互不相关,被c++时库接管并结束后清理
//detach后不可以再用join ,应用较少
//若有引用主线程的值,主线程结束后会引发内存问题
myobj.detach();
if (myobj.joinable())
cout << "能加入" << endl;
else
cout << "不能加入" << endl;
for (int i = 0; i < 20; i++)
{
cout << "main_" << i << endl;
}
test2();
test1();
}
void myprint()
{
for (int i = 0; i < 20; i++)
{
cout << "子线程_" << i << endl;
}
}
void CPrint::operator()()
{
for (auto i = 0; i < 10; i++)
{
cout << "CPring::(" << i << ")" << endl;
}
}
- 代码块2
#include <iostream>
#include<thread>
using namespace std;
class CPrint
{
public:
CPrint(int i) :id(i) { cout << "构造函数"<< this <<" id:" << id << "thread_id = " << this_thread::get_id() << endl; };
CPrint(const CPrint& obj) { id = obj.id; cout << "拷贝构造 " << this << "id:" << id << "thread_id = " << this_thread::get_id() << endl; };
~CPrint() { cout << "析构结束~ " << this << " thread_id = " << this_thread::get_id() << endl; };
mutable int id; //可以在const下改变赋值
void thread_work(int num) { id = 999; cout << "thread_work() " << this << "num:" << num << "thread_id = " << this_thread::get_id() << endl; }
};
void print(const CPrint& obj) //不加const会报错 除非传ref
{
obj.id = 0;
cout << "print() " << &obj << " id:" << obj.id << "thread_id = " << this_thread::get_id() << endl;
}
int main()
{
CPrint obj1(666);
thread mythread1(print, ref(obj1)); //构造和拷贝在主函数完成,使用ref则没有拷贝构造
mythread1.join();
cout << "main() obj1.id="<< obj1.id <<" thread_id = " << this_thread::get_id() << endl;//id已经改变
cout << endl << endl;
CPrint obj2(777);
thread mythread2(&CPrint::thread_work, obj2,555); //调用类方法,也会产生拷贝构造
mythread2.detach();
cout << "main() obj1.id=" << obj2.id << " thread_id = " << this_thread::get_id() << endl; // id不改变
cout << endl << endl;
}