C++ | 多线程编程(一)thread库的使用示例

线程支持库:https://zh.cppreference.com/w/cpp/thread

1. 普通函数线程化

#include <iostream>
#include <thread>

using namespace std;

// 线程函数
void func(int a, int b)
{
	for (int i = 0; i < n; ++i)
	{
		cout << "thread 1 executing a = " << a  << ", b =  " << b << endl;// 毫秒
		std::this_thread::sleep_for(std::chrono::milliseconds(200));
	}
}


int main()
{
	int x = 10;
	std::thread t1(func, x, 100);
	// 睡眠一秒
	std::this_thread::sleep_for(std::chrono::seconds(1));

	t1.join();// 等待t1线程结束,主线程再结束
	return 0;
}


// 进程与线程的区别
/*
	Linux : 进程与线程都是调度单位,每个进程都是独立的,
	Linux 2.6 主函数 <==> 主线程

*/

在这里插入图片描述

1.1 线程具有独立的栈空间

#include <iostream>
#include <thread>

using namespace std;


void func()
{
	int x = 1;
	for (int i = 0; i < 5; ++i)
	{
		cout << "thread  executing x = " << x++ << endl;// 毫秒
		std::this_thread::sleep_for(std::chrono::milliseconds(200));
	}
}
// 问题:线程t1与t2执行后,是否可能会输出 x = 6/7/8/9 的情况?
// 答:不会,因为每个线程都拥有自己的栈空间,对于线程内部来说,
//		x是在线程内部的局部变量,不会被其他线程共享

int main()
{
	std::thread t1(func);
	std::thread t2(func);
	// 睡眠
	std::this_thread::sleep_for(std::chrono::seconds(2));

	t1.join();// 等待t1线程结束,主线程再结束
	t2.join();// 等待t1线程结束,主线程再结束
	return 0;
}

在这里插入图片描述
在这里插入图片描述

对每个进程,Linux内核都把两个不同的数据结构紧凑的存放在一个单独为进程分配的内存区域中:

一个是内核态的进程堆栈stack

另一个是紧挨着进程描述符的小数据结构thread_info,叫做线程描述符。

这两个结构被紧凑的放在一个联合体中thread_union中,

union thread_union
{
   struct thread_info thread_info;
   unsigned long stack[THREAD_SIZE/sizeof(long)];
};

这块区域32位上通常是8K=8192(占两个页框),64位上通常是16K,其实地址必须是8192的整数倍。
原文链接:https://blog.csdn.net/gatieme/article/details/51577479


1.2 线程安全:访问全局变量可能会造成的资源竞态问题
#include <iostream>
#include <thread>

using namespace std;
int g_num = 1;

void func()
{
	for (int i = 0; i < 5; ++i)
	{
		cout << "thread  executing g_num = " << (g_num)++ << endl;// 毫秒
		// g_num ++ 操作的汇编
		// move eax, g_num;		// 取值操作,将g_num的值取到eax寄存器中
		// add  eax, 1;			// 加一操作,eax 进行加一操作
		// mov  g_num, eax;		// 放值操作,将加一后的值从eax放回g_num中 
		std::this_thread::sleep_for(std::chrono::milliseconds(200));
	}
}
// 问题:此时线程t1与t2执行后,是否可能会输出 x = 6/7/8/9 的情况?
// 答:会,因为每个线程除了各自拥有自己的栈空间外,还共享整个程序的全局空间
//		
// 注意:对于全局量 g_num 来说,对主线程、t1线程、t2线程都是可见的。因此,t1线程与t2线程在访问
//		 g_num 时会出现资源的竞争,如果不加以控制就会造成线程不安全的情况。
//		 比如,在我们进行若干次的运行后,输出结果可能出现g_num小于10的情况。
int main()
{
	std::thread t1(func);
	std::thread t2(func);
	// 睡眠
	std::this_thread::sleep_for(std::chrono::seconds(2));

	t1.join();// 等待t1线程结束,主线程再结束
	t2.join();// 等待t1线程结束,主线程再结束
	return 0;
}

在这里插入图片描述

2. 线程函数的参数传递

2.1 传递指针的线程函数

#include <iostream>
#include <thread>

using namespace std;

void func(int a, int *p)
{
	if (nullptr == p) return;
	for (int i = 0; i < 5; ++i)
	{
		cout << "thread  a = " << a++ 
			<< " , *(p) = " << (*p)++ << endl;
		std::this_thread::sleep_for(std::chrono::milliseconds(200));
	}
}

int main()
{
	int a = 10, b = 20;
	std::thread t1(func, a, &b);
	std::thread t2(func, a, &b);

	// 睡眠
	std::this_thread::sleep_for(std::chrono::seconds(2));

	t1.join();// 等待t1线程结束,主线程再结束
	t2.join();// 等待t1线程结束,主线程再结束

	cout << "main: a = " << a << " , b = " << b << endl;
	// 输出a,与b 的值是多少?
	return 0;
}


在这里插入图片描述


2.2 传递引用的线程函数
#include <iostream>
#include <thread>

using namespace std;

void func(int &a)
{
	for (int i = 0; i < 5; ++i)
	{
		cout << "thread  a = " << a++ << endl;
		std::this_thread::sleep_for(std::chrono::milliseconds(200));
	}
}

int main()
{
	int a = 10;
	std::thread t1(func, std::ref(a));
	std::thread t2(func, std::ref(a));

	// 睡眠
	std::this_thread::sleep_for(std::chrono::seconds(2));

	t1.join();// 等待t1线程结束,主线程再结束
	t2.join();// 等待t1线程结束,主线程再结束

	cout << "main: a = " << a << endl;
	// 输出a 的值是多少?
	return 0;
}

当线程函数的参数是引用时,我们需要指明参数的传入方式 std::ref(a);

3. 类成员方法的线程化处理



#include <iostream>
#include <thread>

using namespace std;

// 类成员函数线程化方案
class Object
{
public:
	Object(int _v):m_val(_v) {}
	~Object() {}
	void fun(int a)
	{
		cout << "fun(int a)" << endl;
	}
	void fun_ra(int& a)
	{
		cout << "fun_ra(int &a)" << endl;
	}
	void fun_cra(const int& a)
	{
		cout << "fun_cra(int &a)" << endl;
	}
	void fun_ptr(int* p)
	{
		if (nullptr == p) return;
		cout << "fun_ptr(int *p)" << endl;
	}
private:
	int m_val;
};

int main()
{
	int a = 10;
	Object obj(a);
	std::thread t1(&Object::fun, obj, a);
	std::thread t2(&Object::fun_ra, obj, std::ref(a));
	std::thread t3(&Object::fun_cra, obj, std::cref(a));
	std::thread t4(&Object::fun_ptr, obj, &a);

	t1.join(); t2.join(); t3.join(); t4.join();
	return 0;
}

在这里插入图片描述

3.1 类成员方法在线程化传递参数时,按值传递(形参对象、调用拷贝构造生成对象的副本)


#include <iostream>
#include <thread>

using namespace std;

// 类成员函数线程化方案
class Object
{
public:
	Object(int _v):m_val(_v) {}
	~Object() {}
	void fun(int a)
	{
		cout << "fun(int a)" << endl;
		m_val += 1;
	}
	void fun_ra(int& a)
	{
		cout << "fun_ra(int &a)" << endl;
		m_val += 1;
	}
	void fun_cra(const int& a)
	{
		cout << "fun_cra(int &a)" << endl;
		m_val += 1;
	}
	void fun_ptr(int* p)
	{
		if (nullptr == p) return;
		cout << "fun_ptr(int *p)" << endl;
		m_val += 1;
	}
	int getValue() const { return m_val; }
private:
	int m_val;
};

int main()
{
	int a = 10;
	Object obj(a);
	std::thread t1(&Object::fun, obj, a);
	std::thread t2(&Object::fun_ra, obj, std::ref(a));
	std::thread t3(&Object::fun_cra, obj, std::cref(a));
	std::thread t4(&Object::fun_ptr, obj, &a);

	t1.join(); t2.join(); t3.join(); t4.join();
	cout << obj.getValue() << endl;
	// 这里输出的结果是多少?
	// 答案是10,因为线程函数调用的时候,传递的obj参数是一个对象的拷贝版本(副本)
	//			 并不是obj对象本身
	return 0;
}

线程函数调用的时候,传递的obj参数是一个对象的拷贝版本(副本),我们可以在成员函数调用时,输出其所在对象的this地址,并且我们可以给出其拷贝构造函数,观察其调用具体过程。


#include <iostream>
#include <thread>

using namespace std;

// 类成员函数线程化方案
class Object
{
public:
	Object(int _v):m_val(_v) {}
	// 拷贝构造
	Object(const Object& rhs)
	{
		cout << "copy consstruct , this :" << this << endl;
	}
	~Object() {}
	void fun(int a)
	{
		cout << "fun(int a) , this :" << this << endl;
		m_val += 1;
	}
	void fun_ra(int& a)
	{
		cout << "fun_ra(int &a) , this :" << this << endl;
		m_val += 1;
	}
	void fun_cra(const int& a)
	{
		cout << "fun_cra(int &a) , this :" << this << endl;
		m_val += 1;
	}
	void fun_ptr(int* p)
	{
		if (nullptr == p) return;
		cout << "fun_ptr(int *p) , this :"<< this << endl;
		m_val += 1;
	}
	int getValue() const { return m_val; }
private:
	int m_val;
};

int main()
{
	int a = 10;
	Object obj(a);
	std::thread t1(&Object::fun, obj, a);
	std::thread t2(&Object::fun_ra, obj, std::ref(a));
	std::thread t3(&Object::fun_cra, obj, std::cref(a));
	std::thread t4(&Object::fun_ptr, obj, &a);

	t1.join(); t2.join(); t3.join(); t4.join();
	cout << obj.getValue() << endl;
	// 这里输出的结果是多少?
	// 答案是10,因为线程函数调用的时候,传递的obj参数是一个对象的拷贝版本(副本)
	//			 并不是obj对象本身
	return 0;
}

在这里插入图片描述


3.2 类成员方法在线程化传递参数时,按地址传递(线程函数与当前对象关联)

#include <iostream>
#include <thread>

using namespace std;

// 类成员函数线程化方案
class Object
{
public:
	Object(int _v) :m_val(_v) {}
	// 拷贝构造
	Object(const Object& rhs)
	{
		cout << "copy consstruct , this :" << this << endl;
	}
	~Object() {}
	void fun(int a)
	{
		cout << "fun(int a) , this :" << this << endl;
		m_val += 1;
	}
	void fun_ra(int& a)
	{
		cout << "fun_ra(int &a) , this :" << this << endl;
		m_val += 1;
	}
	void fun_cra(const int& a)
	{
		cout << "fun_cra(int &a) , this :" << this << endl;
		m_val += 1;
	}
	void fun_ptr(int* p)
	{
		if (nullptr == p) return;
		cout << "fun_ptr(int *p) , this :" << this << endl;
		m_val += 1;
	}
	int getValue() const { return m_val; }
private:
	int m_val;
};

int main()
{
	int a = 10;
	Object obj(a);
	std::thread t1(&Object::fun, &obj, a);
	std::thread t2(&Object::fun_ra, &obj, std::ref(a));
	std::thread t3(&Object::fun_cra, &obj, std::cref(a));
	std::thread t4(&Object::fun_ptr, &obj, &a);

	t1.join(); t2.join(); t3.join(); t4.join();
	cout << obj.getValue() << endl;
	// 这里输出的结果是多少?
	
	return 0;
}

在这里插入图片描述


3.3 类的静态函数线程化

#include <iostream>
#include <thread>

using namespace std;

// 类成员函数线程化方案
class Object
{
public:
	Object(int _v) :m_val(_v) {}
	~Object() {}
	static void show() {cout << "Object::show()" << endl;}
private:
	int m_val;
};

int main()
{
	int a = 10;
	Object obj(a);
	std::thread t1(&Object::show);

	t1.join(); 

	return 0;
}

3. 仿函数线程化


#include <iostream>
#include <thread>

using namespace std;

// 仿函数
struct Object
{
	void operator()()
	{
		cout << "operator" << endl;
	}
	void operator()(int a)
	{
		cout << "operator(int a)" << endl;
	}
};


int main()
{
	Object obj;
	std::thread t1(obj);
	std::thread t2(obj,10);

	t1.join(); t2.join();
	return 0;
}

问题:已知全局函数、类成员函数都可以线程化。那么
内联函数、虚函数是否可以线程化?构造函数、拷贝构造函数是否可以线程化?析构函数…

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫RT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值