【Linux 线程介绍】
创始人
2024-02-06 19:24:51
0

Linux 线程

  • 线程一定越多越好吗?
  • 线程的实现方式:
  • API:
  • pthread_exit函数演示
  • 获取线程的返回值
  • 多线程的不安全性
  • 查看进程中的线程数

进程:一个正在运行的程序 ,资源分配的基本单位
线程:进程内部的一条执行序列(路径) 调度的基本单位

线程一定越多越好吗?

首先,我们要明白线程也是需要资源的。

  • 线程的创建和销毁需要花费资源 (陷入内核) 线程栈本身需要占用空间
  • 线程之间上下文切换的适合需要把CPU和寄存器中的数据压到栈中,切换回之后又得恢复
  • 大量线程同时唤醒会使系统经常出现锯齿负载或者瞬间量很大导致宕机
  • 唤醒线程也需要资源

由上面可以看出,线程并不是越多越好,我们什么时候应该使用多线程?

首先我们先了解一下。

程序一般分为两类

  1. IO密集型 (会阻塞)程序涉及一些IO操作,文件操作,网络操作。
  2. CPU密集型 程序大多数都在做运算的

对于多核的主机来说:

CPU密集型的程序:适合设计为多线程,把任务拆分,可以更快

IO密集型的程序:适合设计为多线程,因为如果一些IO没有准备好,系统则会把它放到阻塞队列中,这个时候会把它的时间片给就绪队列中的任务

对于单核主机来说:

IO密集型的程序:适合设计为多线程,当等待IO资源时,便会把时间片让给其它任务

CPU密集型的程序:不合适设计为多线程,单核和多核计算的时间是一样的,但是如果是多线程的话,线程切换会耗费大量的资源和时间

在上面的情况下,我们创建线程的数目一般为主机的核数,如果业务中IO操作比较多的话,那么线程数目也可以稍微多于核数

线程的实现方式:

用户级线程(多个用户线程对应一个内核线程)
在这里插入图片描述
以创建很多数目的线程,这个线程的创建是由线程库中的代码来创建,内核并不参与。
优点 :开销小,
缺点 :无法真正的利用多个处理器,因为内核无法感知到线程的存在,所以这些线程无法被调度的空闲的处理器上去运行

内核级线程(每个用户线程都对应一个内核线程)
在这里插入图片描述
每个线程是由内核创建的
优点 :可以利用多个处理器,可以实现真正意义上的并行,用户级的只能是并发
缺点: 创建开销大

组合级线程(每个用户线程对应不同数量的内核线程)
在这里插入图片描述
组合级则是它俩的组合

在Linux系统上,对于内核来说,没有线程这个概念,它视为这是一个轻量级的进程,线程被内核视为与其他进程共享资源的进程,直接给它一个PCB,也会消耗一个PID,只不过叫线程id,与它的主线程共享进程空间,共享打开的文件资源等等。

API:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);创建线程
thread:指向线程的指针
attr:指定线程的属性
start_coutine:线程函数
arg:传递给线程函数的参数
返回值:成功返回0,失败返回错误码
int pthread_exit(void *retval);终止当前线程,只会终止当前线程,不会影响进程中其它线程的执行。
retval:指定退出信息
int pthread_join(pthread_t thread, void **retval);等待thread指定的线程退出,线程未退出时,该方法阻塞
retval:接收 thread线程退出时,指定的退出信息

pthread_exit函数演示

#include
#include
#include
void * fun(void * arg)
{for(int i=0;i<5;++i){printf("fun \n");}
}
int main()
{pthread_t id;pthread_create(&id,NULL,fun,NULL);for(int i=0;i<5;++i){printf("main \n");}exit(0);
}

执行结果
在这里插入图片描述

#include
#include
#include
void * fun(void * arg)
{for(int i=0;i<5;++i){printf("fun \n");}
}
int main()
{pthread_t id;pthread_create(&id,NULL,fun,NULL);for(int i=0;i<5;++i){printf("main \n");}pthread_exit(NULL);
}

执行结果
在这里插入图片描述
因为在我们上面代码,执行exit(0)的时候,会把整个进程都终止掉,同时也会把我们创建的线程的资源释放,而当我们使用pthread_exit时,只会把主线程终止掉,而不会影响到我们创建的那个线程。

在这里插入图片描述

获取线程的返回值

#include
#include
#include
void * fun(void * arg)
{for(int i=0;i<5;++i){printf("fun \n");}pthread_exit("fun over");
}
int main()
{pthread_t id;pthread_create(&id,NULL,fun,NULL);for(int i=0;i<5;++i){printf("main \n");}char * s;pthread_join(id,(void **)&s);printf("s=%s",s);exit(0);
}

在这里插入图片描述

多线程的不安全性

5个线程同时对一个全局变量加1000

#include
#include
#include
#include
int index =0;
void * fun(void *arg)
{for(int i=0;i<1000;++i){printf("index =%d\n",index++);}}
int main()
{pthread_t arid[5]={0};for(int i=0;i<5;++i){pthread_create(&arid[i],NULL,fun,NULL);}for(int i=0;i<5;++i){pthread_join(arid[i],NULL);}exit(0);}

在这里插入图片描述

最后的结果为4998,而不是5000,这个是为什么,++这个操作并不是原子操作。
我的这个虚拟机是2核的,比如说此时i值为2,当一个线程刚对i值进行i++操作后,此时i值变成3,正要写回,而此时可能时间片到了,然后其它线程进行执行,然后这个线程此时读到i的值为2(因为没有写回),对其加1,然后写回,当时间片又轮到上面的线程时,此时它又按照上面的步骤进行执行,恢复现场,此时寄存器中保存的i值为3,又进行写回,此时i值还为3。
本来经过2次加操作,i的值应该为4,而此时为3。

查看进程中的线程数

需要添加L(小写不行)
![在这里插入图片描述](https://img-blog.csdnimg.cn/70be25076d1c445496fe40d9d68fedcd.pn
线程号是以进程的id号递增的

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...