基本概念
并发:一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。
进程:可执行程序运行,便创建了一个进程。
线程:
- 就是代码的执行通路
- 每个进程都有一个主线程,且唯一,与进程一起产生、结束。
- 多线程,不是越多越好,每个线程需要一个独立的堆栈空间,线程之间切换需切要保存很多中间的状态,切换过多降低程序的运行时间。
开启一个线程
整个进程是否执行完毕,取决于主线程是否执行完毕,即如果主线程执行完毕整个进程也结束,此时如果子线程没有结束,也会被强行结束。因此如果需要让子线程一保持正常运行,主线程一般会保持一致运行,但是也有例外(即detach)。
开启线程
1.需要用到的头文件: #include <thread>,since supported by c++11;
2.线程,就是开辟一个新的通道去执行另一个函数,对象如下:
1)普通函数
2)仿函数(functor):类里面实现operator()
3)Lambda表达式
3.使用的函数:
1)join()
2)detach()
区别:
join()函数,让主线程等待子线程执行完成,相当于阻塞,join执行完毕,join()后面的主线程才会继续执行。这样可以有效防止主程序已经停止运行,而子程序没有运行结束,子程序不得不中断。
detach()函数,让子线程和主线程分开独自执行,无法控制,该子线程由c++运行时库进行控制管理,驻留后台运行,运行结束也是由c++运行时库进行释放相关资源(守护线程)。但是当主线程结束,该子线程也会随即中断结束。
注意事项:
1)在使用detach函数时,注意子线程和主线程之间不要使用“引用”方式来传递参数,否则某一个先结束,释放变量,会导致另一个线程出现bug。
2)join()函数一般置于return语句前面,等待所有的子线程运行结束,主线程再结束。
4.简单案例
#include <thread>
#include <iostream>
void foo() {
std::cout << "subthread" << std::endl;
}
int main(int argc, char const *argv[]){
std::thread thread(foo); // 启动线程foo,并且已经开始执行
thread.join(); // 让主线程等待子线程执行完成,相当于阻塞,join执行完毕,主线程再继续执行
std::cout<<"I am main thread.\n";
return 0;
}
上面关键的两步:
std::thread thread(foo);
thread.join();
首先要创建线程的实例,然后进行阻塞。由于Lambda表达式也可以创建函数,因此也可以用Lambda表达式代替foo,同理functor。
线程传参
1.detach引发的问题
由于detach使得主线程和该子线程进行了分离,主线程不再等待该子线程完成就结束,就可能会导致如下问题:
如果从主线程以引用的方式传参数给子线程,当主线程运行结束,该参数也被释放,就会导致子线程在接受参数时接收到的是一个被释放的内存区域,引发不可预料的问题。
解决办法:
a)若传递int这种简单类型参数,建议都是值传递,不要用引用。防止节外生枝。
b)如果传递类对象,避免隐式类型转换。全部都在创建线程这一行就构建出临时对象来,然后在函数参数里用引用来接;否则系统还会构造一次对象
终极结论:
c)建议不使用detach(),只使用join():这样就不存在局部变量失效导致线程对内存的非法引用问题。
2.获取当前线程id
std::this_thread::get_id()
3.传递类对象的引用到线程函数,std::ref()函数
4.线程函数参数
- 普通函数
- 仿函数(functor)
- 智能指针作为参数
- 类的成员函数指针作为线程参数
(1)functor需要实现operator() ,调用如下,可见只是需要传入一个类的实例即可,自动的调用functor.
输入:
class baz{
public:
void operator()(int n) {
std::cout<<"var address in sub: "<<&n<<"|var value in sub: "<<n<<std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
int n = 0;
};
int main(int argc, char const *argv[]){
baz b;
int n=10;
std::thread t6(b,n); // t6 runs baz::operator() on object b
std::cout<<"var address in main: "<<&n<<"|var value in main: "<<n<<std::endl;
t6.join();
return 0