Day2,线程的创建

thread:位于头文件<thread>中,是一个用于创建线程的类。详情可以在头文件(ctrl+鼠标左键)点进去看。

创建线程的模板:

可执行(调用)对象一般包括:普通函数、函数指针、类内成员函数、仿函数,即重载了operator()运算符的类对象、匿名函数,即Lambda表达式等。

        std::thread 对象名(可执行对象, 可执行对象的参数);如下:

void myprint() { cout << "执行myprint这个函数" << endl; }
void myprint1(const int &num, const int &a) { cout << "执行myprint1这个函数" << "    num的值为:" << num <<endl; }

int main() {
	std::thread obj(myprint);
	obj.join();
	int num = 1, a = 2;
	std::thread obj1(myprint1, num, a);
	obj1.join();

	return 0;
}

        std::thread 对象名(&类名::类内函数名, &类对象 , 参数);  //这里对类对象引用表示可以对该对象的数据进行修改,若不用引用则会调用拷贝构造函数。

主要函数

        join(); 用于阻塞主线程,使得主线程在该线程运行结束后再运行。用法如上。

        detach(); 用于分开主线程和子线程,使其同时独立运行。(用法同join)(慎用,不然可能会传入地址后主线程运行结束而子线程还在运行导致异常)注意:主线程结束后进程结束,所以若用detach,主线程结束后若子线程还未结束的话会在后台中进行直到结束,由运行库释放。

        joinable();同一个线程只能调用一次join或者detach,joinable可以判断该线程是否已经使用过join或者detach,若否,则返回true,若是,则返回false;一般用于如下情况:

std::thread obj(myprint);
if(obj.joinable()){ obj.join(); }

        std::ref(可执行对象的参数);正常情况下传进子线程的带引用的参数都是通过拷贝、复制出一个新的参数传进线程中,在线程中改变该参数并不能影响到线程外的该参数,ref用于告诉(我也不知道告诉编译器还是什么)这个参数在线程内改变可以影响到线程外的该参数(类似于地址传递)。如下:

void myprint1(int &num) {
	num++;
	cout << "执行myprint1这个函数" << "num的值为:" << num <<endl;
}

int main() {
	int num = 1;
	std::thread obj2(myprint1, ref(num));
	obj2.join();
	cout << num << endl; //此时输出2而不是1

	return 0;
}

        this_thread::get_id() / get_id();在同一进程中,所有线程的id都是唯一的,可用于获取某一线程的id。位于可执行对象内时使用this_thread::get_id();位于线程实现后,join()之前时使用对象名.get_id();

void myprint() { 
    cout << "执行myprint这个函数" << endl; 
    cout << "这个线程的id为:" << this_thread::get_id() << endl;
}

int main() {
	std::thread obj(myprint);
    obj.get_id();
	obj.join();

	return 0;
}

常见的可执行对象:

        普通函数:如上。

        类:通过重载类中的()运算符来实现。

class B {
public:
	void operator()(){ cout << "子线程开始" << endl; }
};

int main() {
	B b;
	thread obj(b);
	obj.join();
	return 0;
}

        类的成员函数:thread 线程名(&类名::函数名,  类对象, 函数参数);如下(一定要加&哦)

class C {
public:
	void xiancheng() { cout << "子线程运行" << endl; }
};

int main() {
	C c;
	thread obj1(&C::xiancheng, c);
	obj1.join();

	return 0;
}

使用detach()容易犯的错:由于detach会使子线程与主线程分开,所以操作不当的话会导致传入参数由于主线程结束运行而销毁,但子线程还未复制该参数,导致传入的参数地址无效,发生异常。以下是使用过detach时可能会存在的一些问题:

        类对象

        函数参数中存在引用(&):若是不用引用,则会在运行到子线程时在主线程中先拷贝一遍函数参数,再将拷贝后的参数传入子线程,但是thread会再拷贝一编参数,最后运用拷贝后的参数,即若不使用引用,则参数传递时会拷贝两次。但若是使用引用,则参数只会在thread中被拷贝一次(即虽然是引用,但实质仍是值传递)。下面的例子可以表明该观点:

#include<iostream>
#include<vector>
#include<map>
#include<thread>
using namespace std;

class A {
private:
	int a;
public:
	A() { cout << "调用无参构造函数" << "   此时的线程id为:" << this_thread::get_id() << endl; }
	A(const int& b):a(b) { cout << "调用有参构造函数" << "   此时的线程id为:" << this_thread::get_id() << endl; }
	A(const A& A1):a(A1.a) { cout << "调用浅拷贝构造函数" << "   此时的线程id为:" << this_thread::get_id() << endl; }
	~A() { cout << "调用析构函数" << "   此时的线程id为:" << this_thread::get_id() << endl; }
};
void myprint(const A &a) {  //参数使用引用
	cout << "子线程的id为:" << this_thread::get_id() << endl;
}

void myprint2(const A a) {  //参数不使用引用
	cout << "子线程的id为:" << this_thread::get_id() << endl;
}

int main() {
	cout << "主线程id为:" << this_thread::get_id() << endl;
	A ob;
	thread obj(myprint, ob);
	obj.join();

	thread obj1(myprint2, ob);
	obj1.join();

	return 0;
}

        函数参数中存在指针时:若是用指针,则是地址传递,若此时主线程结束,则该指针指向的地址会被释放,导致子线程中的指针变为NULL或野指针,发生异常。

        函数传入实参要避免隐式类型转换:当函数实参需要隐式类型转换时,会在子线程中调用主线程的函数,若子线程中未开始类型转换时主线程结束,则类型转换所需函数会被销毁,子线程调用时会返回异常。所以在使用时,要避免隐式类型转换的发生,可以主动强制类型转换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值