并发-创建线程

122 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天点击查看活动详情

创建线程

包含头文件#include<thread>

用函数创建线程

线程入口函数是可以给多个线程使用的

/*线程入口函数*/
void threadBegin()
{
}
thread a(threadBegin);//创建一个线程对象 括号里面需要放一个可调用对象用来构造对象  这个函数就变成这个线程的执行起点

创建线程的函数传参问题

void mprint(const int &a,char* b)
{
    cout << a << endl;
    cout << b << endl;
}
int main()
{  
    int a = 0;
    char b[] = "abcd";
    thread mobj(mprint,a,b);//这种方式将参数传入
    //mobj.detach(); 
    mobj.join();
    return 0;
}

虽然用的是const int &a也就是引用方式 实际上子线程内部的a和主线程中的a地址不一样 并不是真的引用了 实际上是值传递 解决方法用thread mobj(mprint,ref(a),b); 但是如果通过指针传递 子线程中的b和主线程的b是一个地址 所以这样是很危险的

类型转化出现的问题

因为直接传指针是不安全的 所以我们把这个指针存储的值转换成string会好点 就会变成值传递 但是还是会出现问题 因为并发的原因我们并不知道 char b[]是什么时候转换成string的 可能出现char b[]已经在主函数中被回收了 再进行string的类型转换 (这里没懂 如果会出现这个问题 那为什么int a可以实现传值 难道不会出现int a在主函数中被收回了 再值传递给子线程的a吗)

void mprint(const int &a,const string& b)
{
    cout << a << endl;
    cout << b << endl;
}
int main()
{  
    int a = 0;
    char b[] = "abcd";
    thread mobj(mprint,a,b);//正确方式是thread mobj(mprint,a,string(b)); 生成一个临时的string
    //mobj.detach(); 
    mobj.join();
    return 0;
}
证明string(b)是可行的
class A {
public:
	double x, y;
	A()
	{
		cout << "构造函数" << endl;
	}
	A(int i) {//类型转换构造函数
		cout << "类型转换构造函数" << endl;
		x = i; y = 0;
	}
	A(const A& c) :x(c.x), y(c.y)
	{
		cout << "调用了拷贝构造函数" << endl;
	}
	~A()
	{
		cout << "析构了" << endl;
	}

};
void start(const A& a)
{
	cout << "进入子线程了" << endl;
}
int main()
{
	int x = 1;
	thread mobj(start, x);
	mobj.detach();
	return 0;
}

请添加图片描述 这里很明显的说明了 可能在主线程结束后再执行A a = x的类型转换 所以并不是完全执行完thread mobj(start,x)里面的所有构造 主线程才会继续执行 所以就会出现出现前面的char b[]已经在主函数中被回收了 再进行string的类型转换 当我们改成A(x)时 请添加图片描述 很明显 当我们临时创建一个A类对象时 是一定先执行的 原因是什么呢?我觉得应该是A(x)这句话一定是主线程执行的 所以一定可以在主线程结束之前执行 并且 我们也能看到调用了拷贝构造函数 说明 此时已经将临时创建的A对象拷贝给了线程中的a

通过线程id证明是在主线程中创建的临时对象

通过在构造函数中加入获取线程id this_thread::get_id()判断对象的创建是在子线程中还是主线程中

class A {
public:
	double x, y;
	A()
	{
		cout << "构造函数" << endl;
		cout << "线程的id:" << this_thread::get_id() << endl;
	}
	A(int i) {//类型转换构造函数
		cout << "类型转换构造函数" << endl;
		cout << "线程的id:" << this_thread::get_id() << endl;
		x = i; y = 0;
	}
	A(const A& c) :x(c.x), y(c.y)
	{
		cout << "调用了拷贝构造函数" << endl;
		cout << "线程的id:" << this_thread::get_id() << endl;
	}
	~A()
	{
		cout << "析构了" << endl;
		cout << "线程的id:" << this_thread::get_id() << endl;
	}

};
void start(const A& a)
{
	cout << "进入子线程了" << endl;
	cout << "子线程的id:" << this_thread::get_id() << endl;
}
int main()
{
	cout << "主线程的id:" << this_thread::get_id() << endl;
	int x = 1;
	thread mobj(start, A(x));
	//mobj.detach();
	mobj.join();
	//Sleep(10);
	cout << "主线程结束" << endl;
	return 0;
}

请添加图片描述

用类对象创建线程

当用类对象创建线程的时候 会复制一个类的对象到线程中去 所以如果主线程中的类对象销毁了 子线程中的类对象并不会被销毁

class A
{
public:
    A()
    {
        cout << "A的构造函数"<<endl;
    }
    A(const A& a)
    {
        cout << "A的拷贝构造函数" << endl;
    }
    void operator()() 
    {
        cout << "我是类";
    }
    ~A()
    {
        cout << "A的析构函数" << endl;
    }

};
int main()
{ 
    A a;
    thread mobj(a);
    //mobj.detach(); 
    mobj.join();
    return 0;
}

请添加图片描述

会发现调用A的拷贝构造函数 说明是拷贝了一个A类对象到子线程中去

lambda表达式创建

auto mylambda = []{
	cout<<"lambda执行开始"<<endl;
	cout<<"lambda执行结束"<<endl;
};
thread mobj(mylambda);

类成员函数指针创建

注意成员函数前面必须加& 同样 这样传入的类对象一样会生成一个新的临时类对象 并不是将主线程中的类对象传入

class A {
public:
	void thread_work(int &a)
	{
		cout << "进入子进程" << endl;
		a = 2;
	}
	void operator()(int &a)//重载()操作符
	{
		cout << "进入子进程" << endl;
		a = 2;
	}
};
int main()
{
	int x = 1;
	A a;
	thread mobj(&A::thread_work,a,ref(x));//类成员函数指针 类对象 传入参数
	/*通过类成员函数重载() 实现线程创建*/
	//thread mobj(a,x);//类成员函数指针 类对象 传入参数
	mobj.join();
	cout << "主线程中的x:" << x << endl;
	cout << "主线程结束" << endl;
	return 0;
}

join()

阻塞主线程 让主线程等待子线程执行完毕 然后子线程和主线程汇合

detach()

将子线程和主线程分离

void mprint()
{
    cout << "第一个线程"<<endl;
    cout << "第一个线程" << endl;
    cout << "第一个线程" << endl;
    cout << "第一个线程" << endl;
}
int main()
{
    thread mobj(mprint);
    mobj.detach();
    cout << "main"<<endl;
    cout << "main" << endl;
    cout << "main" << endl;
    cout << "main" << endl;
}

detach()后子线程和主线程失去联系 子线程驻留在后台运行 子线程由C++运行时库接管 子线程执行完成后由运行时库清理相关资源(守护线程) 也就是此时主线程可以和该子线程并发执行 与join相对立 在子线程detach后不能再join回来 会报错

joinable()

判断是否能使用join或者detach if(mobj.joinable() == true)

注意

在子线程中通过引用传递参数是没办法实现更改的

因为实际上编译器是会创建一个临时变量

void start(const int &a)
{
	cout << "进入子线程了" << endl;
	int& b = const_cast<int&>(a);
	b = 2;
	cout << "子线程结束" << endl;
}
int main()
{
	int x = 1;//并没有变成2
	thread mobj(start, x);
	mobj.join();
	cout << "主线程中的x:" << x << endl;
	cout << "主线程结束" << endl;
	return 0;
}

请添加图片描述 虽然用的是const int &a也就是引用方式 实际上子线程内部的a和主线程中的a地址不一样 并不是真的引用了 实际上是值传递 创建了一个临时值获取了传递进来的a的值 解决方法 std::ref()

void start(int &a)
{
	a = 2;
}
int main()
{
	int x = 1;
	thread mobj(start, ref(x));
	mobj.join();
	cout<<x;//2