C++中多线程的创建方式总结
文章目录
使用函数作为参数创建线程
函数无参
使用一个无参函数创建线程,thread 的构造接受一个参数,即函数名。
//编写一个函数作为线程的执行路径 void myprint() { cout << "我的线程开始运行" << endl; cout << "我的线程开始运行" << endl; cout << "我的线程开始运行" << endl; cout << "我的线程开始运行" << endl; cout << "我的线程开始运行" << endl; cout << "我的线程开始运行" << endl; } //在main中: //使用函数作为参数创建一个线程对象 thread mythread(myprint); //join方法将使主线程阻塞,等待子线程执行完毕,子线程执行完毕后再“汇合”,主线程继续执行。 mythread.join(); //detach方法不会使主线程阻塞,它会与主线程一起运行, 当主线程执行完毕而调用detach的线程还未执行完毕h时: //它会被c++运行时库接管 //mythread.detach();
函数有参
使用一个有参函数创建线程,thread 的构造接受第一个参数为函数名,之后接受若干参数,分别为函数的各个参数。
//此处引用作为参数应该使用const修饰,否则将报错 void print(const int& i, char* p) { cout << i << endl; cout << p << endl; } //在main中: int i = 10; char chs[] = "hello world!"; thread mythread10(print, i, chs); mythread10.join();
可以通过将类作为函数参数, 并重写类中构造,析构,拷贝构造等方法的方式测试到:通过函数参数传递数据的过程是传递的数据的拷贝(即使函数形参数使用的是引用)。
//类Ta代码在本文后面 void fun(const Ta& a) { cout << "a.i=" << a.i << endl; } int main() { Ta t; thread mythread2(fun, t); mythread2.join(); return 0; }
在多线程中,无论采用值传递还是引用传递,编译器为了数据安全,都会创建一个新对象传递给其他线程(这里指mythread),为了使传递给其他线程的不是新对象,而是原来对象的引用(即希望线程中对该数据的修改能够影响主线程中的值),应该使用其他方式。
使子线程结果影响主线程unique_ptr和ref()
使用独占指针作为函数参数unique_ptr,可以使得传递到子线程的不是副本。
void unique_ptr_fun(unique_ptr<Ta> uniqueTa) { cout << "OK" << endl; } //main中 unique_ptr<Ta> uniqueTa(new Ta(10)); //这种调用将报错,应该使用将对象所有权转移的move()函数 //thread mythread2(unique_ptr_fun, uniqueTa); thread mythread2(unique_ptr_fun, move(uniqueTa)); mythread2.join();
由于使用智能指针,子线程中的指针指向一块主线程创建的内存,所有要使用join而不是detach,否则会可能让子线程中指针指向一块被释放了的内存的危险。
而且这样将导致main中的指向该块数据的智能指针不能使用了。所有,应该用ref方法来完成。
这种用std::ref的调用方式不会调用拷贝构造函数,而是直接将原来对象传递过去,其他线程对数据的修改会影响主线程,即这种方式失去了对原始数据的保护.
void fun2(Ta& ta) { ta.i = 100; } //main中: Ta ta(10); cout << "调用mythread2之前 m=" << ta.i << endl; //通过输出的对象地址相同可以发现这一点 thread mythread2(fun2, std::ref(ta)); mythread2.join(); cout << "调用mythread2之后 m=" <<ta.i<< endl;
数据失去保护后,如果用detach(),将导致严重问题,一定要用join().
使用类作为线程对象的参数
通过重载运算符()后,传入对象,编译器便从()的重载函数作为线程的执行入口。这里的创建线程调用了拷贝构造函数
class Ta { int i; public: Ta(int _i) :i(_i) { cout << "构造函数执行" << endl; } Ta(const Ta& t) :i(t.i) { cout << "拷贝构造函数执行" << endl; } ~Ta() { cout << "析构函数执行" << endl; } public: void operator()() //不能带参数 { cout << "对象中的线程开始执行" << endl; cout << "对象中的线程开始执行" << endl; cout << "对象中的线程开始执行" << endl; } }; //在main中 Ta ta; thread mythread(ta); mythread.join(); //由于创建thread对象的时候调用拷贝构造函数将ObjectA深拷贝到Ta的函数内,所以当主线程退出后, //mythread2仍然能继续执行,但是由于主函数结束,所以控制台输出可能不完整 //mythread2.detach();//也可以用detach()
使用类的成员函数作为线程入口
在类中添加应该public函数
void thread_work(int i) { cout << i << endl; }
传入多个参数,第一个是函数地址,第二个是对象名,然后依次是函数参数
//使用成员函数作为线程入口 Ta obj(23); //传入多个参数,第一个是函数地址,第二个是对象,第三个是函数参数 thread mythread4(&Ta::thread_work, obj, 100); mythread4.join();
这里如果第二个参数用引用,即写成&obj,则传递的是原对象,而不是其拷贝
用lambda表达式定义线程
auto mylambdathread = [] { cout << "用lambda表达式创建的表达式开始执行" << endl; }; thread mythread3(mylambdathread); mythread3.join();
this_thread::get_id() 获取当前所在线程的id
joinable() joinable判断是否可以join或detach
- java多线程总结一:线程的两种创建方式及优劣比较
- java多线程总结一:线程的两种创建方式及比较
- 04.多线程--06.【同步方式在线程两种创建方式中的可行性】【同步代码块和同步函数的关系】【多线程程序设计思路总结】
- java多线程总结一:线程的两种创建方式及优劣比较
- java多线程总结一: 线程的两种创建方式及优劣比较
- java多线程总结一: 线程的两种创建方式及优劣比较
- c++创建多线程的三种方式的比较 和 多线程通信
- java多线程总结一:线程的两种创建方式及优劣比较
- java多线程总结一:线程的两种创建方式及优劣比较
- java多线程总结一:线程的两种创建方式及优劣比较
- Java【多线程知识总结(5)】比较继承Thread类创建线程和实现Runnable接口创建线程这两种方式
- c++多线程创建的几种方式
- Java【多线程知识总结(5)】比较继承Thread类创建线程和实现Runnable接口创建线程这两种方式
- java多线程总结一: 线程的两种创建方式及优劣比较
- Java多线程学习总结--线程概述及创建线程的方式(1)
- java多线程总结一:线程的两种创建方式及优劣比较
- Java多线程基础学习之线程的创建方式总结
- Java多线程并发01——线程的创建与终止,你会几种方式
- C++ | 多线程(Thread、线程创建、线程池)
- Java多线程:创建线程的两种实现方式