【Linux学习】- POSIX多线程技术

目录

一:引言

二:什么是线程【进程包含线程】

三:fork和创建新线程的区别

四:线程的优点和缺点

五:CPU调度 CPU时间轮片(CPU时间片轮转)

六:线程函数库

七:代码学习【线程】


一:引言

做服务器和客户端时候,发现进程间数据不共享(一个进程表示一个客户端,可以做到数据共享,但要使用到几乎所有的IPC技术 比较麻烦),相关聊天业务无法实现,因此就需要学习新的技术完成业务。在此学习一下POSIX多线程技术,用一个线程保存一个客户端的方式,想要实现一个服务器中两个客户端之间数据的交互,最适合使用的就是线程。

二:什么是线程【进程包含线程】

•在一个程序里的多个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
•一切进程至少都有一个执行线程【线程是包含在进程内部的,计算机识别运行的都是线程,我们看到的任务管理器是进程】
•进程是资源分配的基本单位,线程是cpu调度的基本单位 【表面上我们看到的是进程,但一个进程至少有一个执行线程,实际上也就是说,计算机底层上来看是线程被CPU给调用了】【一个进程中可以开无限多个线程,当然前提是内存硬件方面允许的前提条件下,现实中测试过进程差不多开三百个线程就开不下去了】【进程只是一个分配的单位,怎么计算内存空间怎么计算耗费资源,一个进程内部可以包含多条线程,线程可以资源共享】

为什么线程能够共享资源 :因为线程是包含在一个进程里,而这个进程分配资源,这个进程里包含多条线程,这些线程是在共享这个进程中的所有资源。

三:fork和创建新线程的区别

•当一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程将拥有它自己的变量和它自 己的PID。这个新进程的运行时间是独立的,它在执行时几乎完全独立于创建它的进程
•在进程里面创建一个新线程的时候,新的执行线程会拥有自己的堆栈(因此也就有自己的局部变量),但要与它的创建者【进程】共享全局变量、文件描述符、信号处理器和当前的子目录状态。

四:线程的优点和缺点

优点: 

•创建一个新线程的代价【计算机CPU内存电源GPU】要比创建一个新进程小得多【进程包含线程】
•与进程之间的切换相比,线程之间的切换需要操作系统做的工作至少在理论上要少很多
•线程占用的资源要比进程少很多

进程间共享数据需要IPC ,线程之间共享全局变量(线程在同一个进程中)

缺点:

•编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的
•调试一个多线程程序也比调试一个单线程程序困难得多

五:CPU调度 CPU时间轮片(CPU时间片轮转)

一个核心单位时间可以同时跑二十个线程 ,CPU满载情况下,同一时间单位时间1ms可以跑12*20=240个线程。如果是以前的电脑单核心单线程,1*1=1个线程。由此可见以前和现在相比科技的进步还是非常大的。

CPU满载,10*16=160线程。相比上面240少了80。那如果假设是一个进程中包含两个线程 ,就少跑了40个进程。由此可见,计算机根据不同硬件性能列出不同的价位,都是根据自身实际需求选择计算机的。

CPU时间轮片。1s=1000ms ,以前的电脑单核心单线程,1*1=1个线程,以单位1ms来看,都有1000个线程。同时开多个软件,只是使用了一个软件,而不使用其他的软件,正在使用的是前台,没有在使用的叫后台。这个时候CPU时间轮片就会多关照前台(用户正在使用的软件)。

1s = 1000ms  100个线程,按照平均来看,会轮转10次,但实际不是。假设一个前台程序会用900ms都在跑一个线程。那剩余99个线程(后台软件)只用100ms就解决了。由此可见,CPU是非常灵活的,轮片是按需分配的。

小结:每个线程的执行时间随机 同时 每个线程的执行顺序也是随机的。【执行时间随机 执行顺序随机】CPU时间轮片 无法通过代码改变。能做到的只有线程的创建,线程的暂停(停止),但线程什么时候被CPU调用不知道。CPU只能保证 是一个线程 就一定会跑 但什么时候跑 跑什么时候CPU说了算(从线程来看 是需要CPU执行权的争抢  从CPU来看 每一个都一样 每一个都至少跑一次)

(1核心 4线程 和 2核心 2线程 表面上是一样的,但是实际上最好选单核心,买电脑不能只注重核心数,要综合考虑线程的,单位核心线程越多的电脑越好) 【苹果电脑软件和硬件都是自己生产的,生态比较好,算法(内存 CPU调用)的结构比较好(就比如安卓手机经常需要清理后台,而苹果不用,因为只有在运行软件的时候才会占用内存,不运行软件的时候整个内存都会吐出来,因此,你在使用苹果手机,都是一个软件一个软件地用,基本不会出现有卡顿的现象),反正选什么品牌还是看自身具体有什么需求吧】

六:线程函数库

•与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
•要使用这些函数库,要通过引入头文件<pthread.h>
•链接这些线程函数库时要使用编译器命令的“-lpthread”选项

pthread_create函数

作用:创建一个新的线程

•thread:新线程创建成功后,保存新线程的标识符
•attr:设置线程的属性,一般不需要什么特殊的属性,直接传      NULL即可
•start_routine: 是个函数地址,线程启动后要执行的函数
•arg:传给线程启动函数的参数

七:代码学习【线程】

引用了头文件,还是出现未定义引用。

原因:VS默认情况下不支持多线程。

解决: 右键工程属性

链接器 命令行下 输入-pthread 点击应用 点击确定 即可 

1.正常运行 在接收程序时候,进程结束,主子线程也都会结束【因为进程包含线程】。

每个线程的执行时间随机 同时 每个线程的执行顺序也是随机的。【执行时间随机 执行顺序随机】

#include<iostream>
#include<pthread.h>
#include<unistd.h>

using namespace std;

void* pthread_function(void* pv)
{
	while (1)
	{
		cout << "子进程.............run" << endl;
		sleep(1);
	}
}

int main()
{
	pthread_t threadid;

	pthread_create(&threadid, NULL, pthread_function, NULL);
	
	while (1)
	{
		cout << "主进程的 主线程...............run" << endl;
		sleep(1);
	}

	return 0;
}

若是虚拟机设置如下,就可以8条线程同时走

就会有如下图运行结果 

若是ubuntu下的手动编译,命令需要写上-pthread,否则会编译不过去,需要注意!

2. 同一进程,不同线程之间,数据共享。【线程间可以将全局变量进行共享】

#include<iostream>
#include<pthread.h>
#include<unistd.h>

using namespace std;

int number = 0;

void* pthread_function1(void* pv)
{
	while (1)
	{
		number++;
		cout << "子进程1.............run --- number =  " << number << endl;
		sleep(1);
	}
}

void* pthread_function2(void* pv)
{
	while (1)
	{
		number++;
		cout << "子进程2.............run --- number =  " << number << endl;
		sleep(1);
	}
}

int main()
{
	pthread_t threadid;

	pthread_create(&threadid, NULL, pthread_function1, NULL);
	pthread_create(&threadid, NULL, pthread_function2, NULL);
	
	while (1)
	{
		cout << "主进程的 主线程...............run" << endl;
		sleep(1);
	}

	return 0;
}

3. 新的执行线程会拥有自己的堆栈(因此也就有自己的局部变量)【局部变量线程之间数据不共享】

#include<iostream>
#include<pthread.h>
#include<unistd.h>

using namespace std;



void* pthread_function1(void* pv)
{
	int x = *(int*)pv;
	while (1)
	{
		x++;
		cout << "子进程1.............run --- number =  " << x << endl;
		sleep(1);
	}
}

void* pthread_function2(void* pv)
{
	int y = *(int*)pv;
	while (1)
	{	
		y++;
		cout << "子进程2.............run --- number =  " << y << endl;
		sleep(1);
	}
}

int main()
{
	int number = 0;

	pthread_t threadid;

	pthread_create(&threadid, NULL, pthread_function1, &number);
	pthread_create(&threadid, NULL, pthread_function2, &number);
	
	while (1)
	{
		cout << "主进程的 主线程...............run" << endl;
		sleep(1);
	}

	return 0;
}

4.以传参的形式 将原地址上数据++进行测试 依旧可以实现线程之间数据共享

#include<iostream>
#include<pthread.h>
#include<unistd.h>

using namespace std;

void* pthread_function1(void* pv)
{
	while (1)
	{
		(*(int*)pv)++;
		cout << "子进程1.............run --- number =  " << (*(int*)pv) << endl;
		sleep(1);
	}
}

void* pthread_function2(void* pv)
{
	while (1)
	{	
		(*(int*)pv)++;
		cout << "子进程2.............run --- number =  " << (*(int*)pv) << endl;
		sleep(1);
	}
}

int main()
{
	int number = 0;

	pthread_t threadid;

	pthread_create(&threadid, NULL, pthread_function1, &number);
	pthread_create(&threadid, NULL, pthread_function2, &number);
	
	while (1)
	{
		cout << "主进程的 主线程...............run" << endl;
		sleep(1);
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_56051805/article/details/125879821