c++11并发与多线程(第二讲)
#include <iostream>
#include <thread>
#include <string>
using namespace std;
/*
int main()
{
范例演示线程运行的开始和结束
程序运行起来,生成一个进程,该进程所属的主线程开始自动运行;
//实际上是主线程执行,主线程从mian函数开始执行,函数返回则线程执行结束。
cout << "Hello world!" << endl;
return 0;
}
*/
//自己创建一个线程,也需要从一个函数开始运行;
void my_print()
{
cout << "我的线程 Hello my_print!" << endl;
cout << "我的线程 Hello my_print!" << endl;
cout << "我的线程 Hello my_print!" << endl;
cout << "我的线程 Hello my_print!" << endl;
cout << "我的线程 Hello my_print!" << endl;
cout << "我的线程 Hello my_print!" << endl;
cout << "我的线程 执行完成了。。。" << endl;
}
int main()
{
/*
主线程从main()开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦函数运行完毕,就代表着我们这个线程运行结束。
整个进程是否执行完毕的标志是:主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了;
此时,一般情况下:如果其他子线程还没有执行完毕,那么整个子线程也会被操作系统强行终止。
所以一般情况下:结论,如果想保持子线程(自己用代码创建的线程)的运行状态,那么就需要让主线程一直保持运行,不要让它运行完毕
这个规律有例外,老师后续会解释这种例外,大家目前先这样理解和记忆.
a)包含一个头文件 #include <thread>
b)初始函数要写。
这是需要明确一点:有2个线程在跑,相当于整个程序的执行有两条线在同时走,所以可以同时干两件事。
即使一条线被堵住了,也不影响另一条线的执行。
(1.1)thread:是一个标准库里的类
std::thread my_thread1(my_print);
a)my_print是一个可调用对象
b)创建了线程,线程执行的入口函数my_print
c)my_print线程开始执行。
(1.2)join():加入、汇合,说白了就是阻塞,柱塞主线程,让子线程执行完毕,然后子线程和主线程,
让子线程和主线程汇合,然后主线程再往下走。
my_thread1.join();主线程柱塞在这里等待my_print执行完,这个join()就执行完毕主线程就继续向下执行。
如果主线程执行完毕了,但是子线程还没有执行完成,这种程序是不合格的,程序也是不稳定的。
一个书写良好的程序,应该是主线程等待子线程执行完成后,自己才能退出。
(1.3)detach(): 分离,也就是主线程不和子线程汇合了,你主线程执行你的,我子线程执行我的,你主线程也不必等我子线程运行结束
为什么引入detach()我们创建了很多子线程,让主线程逐个等待子线程结束,这种编程方法不好,所以引入了detach();
其实最好的做法是让主线程等待每一个子线程执行结束。
一旦detach后,与主线程子关联的thread对象,就会失去与这个主线程的关联。
这时这个子线程就会驻留在后台运行,主线程与子线程失去联系
子线程就会被c++运行时库接管了。当子线程执行完毕后,由运行时库负责清理这个线程相关的资源(守护线程)
一旦调用了deatch(),就不能再使用join()了,否则系统报异常。
(1.4) joinable():判断是否可以join()或者detach()。
如果返回true(表示可以join也可以detach)
如果返回false表示不可以join和detach
*/
std::thread my_thread1(my_print);
cout <<my_thread1.joinable()<< endl;//1 true
//my_thread1.join();
my_thread1.detach();
cout <<my_thread1.joinable()<< endl;//0 false
cout << "Hello world!" << endl;
return 0;
}
二:其他创建线程的手法
(2.1)用类,以及一个问题范例
#include <iostream>
#include <thread>
#include <string>
using namespace std;
class A
{
public:
void operator()() //不能带参数
{
cout << "我的线程 operator执行了" << endl;
cout << "我的线程 operator执行了" << endl;
cout << "我的线程 operator执行了" << endl;
cout << "我的线程 operator执行了" << endl;
}
};
int main()
{
A a1;
std::thread my_thread2(a1);//使用类对象创建线程,线程函数是operator()
cout <<my_thread2.joinable()<< endl;//true
my_thread2.join();
//my_thread1.detach();
cout <<my_thread2.joinable()<< endl;//0 false
cout << "Hello world!" << endl;
return 0;
}
#include <iostream>
#include <thread>
#include <string>
using namespace std;
class A
{
public:
int &m_i;
A(int &i):m_i(i)
{
cout << "构造函数执行了" << endl;
}
A(const A &a1):m_i(a1.m_i)
{
cout << "拷贝构造函数执行" << endl;
}
~A()
{
cout << "析构函数执行" << endl;
}
void operator()()//不能带参数
{
cout << "我的线程 operator执行了" << endl;
cout << "m_i的值为:"<<m_i<< endl;
//这边的m_i来着主线程,如果主线程结束了,回收了m_i的内存,那么就会出问题,打印的值无效
cout << "m_i的值为:"<<m_i<< endl;
cout << "m_i的值为:"<<m_i<< endl;
cout << "m_i的值为:"<<m_i<< endl;
}
};
int main()
{
/*
二:其他创建线程的手法
(2.1)用类,以及一个问题范例
*/
int my_i=6;
A a1(my_i);
std::thread my_thread2(a1);//使用a1可调用类对象
/*一旦调用了detach(),如果主线程结束了,那么这边的a1对象还存在吗?
实际上这个a1是被复制到线程中去了。所以执行完主线程后a1会被销毁。
但是复制到子线程中的a1对象依然存在,所以,只要a1对象中没有引用和指针,那么就没有什么问题
*/
//my_thread2.join();
my_thread2.detach();
cout <<my_thread2.joinable()<< endl;//0 false
cout << "Hello world!" << endl;
return 0;
}
使用 my_thread2.detach();的结果,从结果看出m_i<值出问题了
使用 my_thread2.join();的结果,
第一次调用的析构函数是复制进子线程的对象被析构了
第二次析构是主线程中的对象a1被析构
int main()
{
/*
二:其他创建线程的手法
(2.2)使用lambda表达式
*/
auto my_lambda_th = []
{
//自己创建一个线程,也需要从一个函数开始运行;
cout << "我的线程 Hello my_lambda_thread!" << endl;
cout << "我的线程 Hello my_lambda_thread!" << endl;
cout << "我的线程 Hello my_lambda_thread!" << endl;
cout << "我的线程 Hello my_lambda_thread!" << endl;
cout << "我的线程 Hello my_lambda_thread!" << endl;
cout << "我的线程 Hello my_lambda_thread!" << endl;
cout << "我的线程 执行完成了。。。" << endl;
}; 这边的分号不要丢了
std::thread my_thread3(my_lambda_th);//lambda表达式
my_thread3.join();
//my_thread2.detach();
cout << "Hello world!" << endl;
return 0;
}