Linux基础知识点-(七-线程)

目录

一、线程和进程

1.1 线程的基本概念

1.2 线程的优缺点

二、创建线程

2.1 pthread_create() - 创建线程函数

三、线程属性

四、线程退出

4.1 pthread_exit()

4.2 pthread_join() 

4.3 pthread_detach() 


一、线程和进程

1.1 线程的基本概念

在很多Linux的书籍基本上都是这样子描述进程(process)和线程(thread)的: 进程是资源管理的最小单位,线程是程序执行的最小单位

在操作系统设计上,从进程演化出线程。 线程的出现最主要的目的就是减小进程上下文切换开销,进程是资源管理的最小单位,那么每个进程都拥有自己的数据段、代码段和堆栈段, 这必然就造成了进程间切换时都要进行比较复杂的上下文切换等动作,因为要保存当前进程上下文的内容, 还要恢复另一个进程的上下文。如果是经常切换进程的话,这样子的开销就过于庞大。因为在进程切换上下文时, 需要重新映射虚拟地址空间、进出OS内核、寄存器切换,还会干扰处理器的缓存机制。 因此为了进一步减少CPU在进程切换时的额外开销,Linux进程演化出了另一个概念——线程。

线程是操作系统能够调度和执行的基本单位,在Linux中也被称之为轻量级进程。在Linux系统中, 一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个CPU上执行。在CPU眼中只认PCB(task_struct),不分进程和线程,进程中的每一个task_struct就是一个线程,他们指向同一个地址空间。

task_struct是进程内部的一个执行流,进程可以重新理解为内部只有一个执行流的进程,多线程可以理解为内部具有多个执行流的进程。 

1.2 线程的优缺点

线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多
  • 能充分利用多处理器的可并行数量
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

线程的缺点: 

  • 性能损失
  • 健壮性降低
  • 缺乏访问控制
  • 编程难度提高

线程共享进程数据,但也拥有自己的一部分数据

  •  线程ID
  • 一组寄存器
  • errno
  • 信号屏蔽字
  • 调度优先级

进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

  • 文件描述符表
  • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  • 当前工作目录
  • 用户id和组id

二、创建线程

可移植操作系统接口 (Portable Operating System Interface,缩写为POSIX), POSIX是IEEE为了在各种UNIX操作系统上运行软件而定义API接口等一系列互相关联的标准的总称, 其正式称呼为IEEEStd 1003,而国际标准名称为ISO/IEC9945,此标准源于一个大约开始于1985年的项目。 POSIX这个名称是由理查德·斯托曼(RMS)应IEEE的要求而提议的一个易于记忆的名称。 它基本上是Portable Operating System Interface(可移植操作系统接口)的缩写, 而X则表明其对Unix API的传承。 

在Linux系统下的多线程遵循POSIX标准,而其中的一套常用的线程库是 pthread,它是一套通用的线程库,是由 POSIX提出的,因此具有很好的可移植性,Linux多线程编程也正是使用它,在使用时必须包含以下头文件: 

#include <pthread.h>

除此之外在链接时需要使用库libpthread.a。因为pthread的库不是Linux系统的库, 所以在编译时要加上-lpthread 选项。

2.1 pthread_create() - 创建线程函数

pthread_create()函数是用于创建一个线程的,创建线程实际上就是确定调用该线程函数的入口点, 在线程创建后,就开始运行相关的线程函数。函数原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

参数说明

  • thread:指向线程标识符的指针。

  • attr:设置线程属性,attr为NULL表示默认属性。

  • start_routine:start_routine是一个函数指针,指向要运行的线程入口,即线程运行时要执行的函数代码。

  • arg:给start_routine指向的函数传入的参数。

  • 返回值:若线程创建成功,则返回0。若线程创建失败,则返回对应的错误代码。

三、线程属性

 上面pthread_create中需要以线程属性作为输入参数,在Linux中线程属性结构如下:

typedef struct
{int                   etachstate;      //线程的分离状态int                   schedpolicy;     //线程调度策略structsched_param     schedparam;      //线程的调度参数int                   inheritsched;    //线程的继承性int                   scope;           //线程的作用域size_t                guardsize;       //线程栈末尾的警戒缓冲区大小int                   stackaddr_set;   //线程的栈设置void*                 stackaddr;       //线程栈的位置size_t                stacksize;       //线程栈的大小
}pthread_attr_t;

线程的属性非常多,而且其属性值不能直接设置,须使用相关函数进行操作。线程属性主要包括如下属性: 作用域(scope)、栈大小(stacksize)、栈地址(stackaddress)、优先级(priority)、 分离的状态(detachedstate)、调度策略和参数(scheduling policy and parameters)。 默认的属性为非绑定、非分离、1M的堆栈大小、与父进程同样级别的优先级,无其他特别需求,是可以不需要考虑线程相关属性的,使用默认的属性即可。

四、线程退出

4.1 pthread_exit()

在线程创建后,系统就开始运行相关的线程函数,在该函数运行完之后,该线程也就退出了, 这是线程的一种隐式退出的方法,这与进程的退出差不多,进程完成工作后就会退出。 而另一种退出线程的方法是使用pthread_exit()函数,让线程显式退出,这是线程的主动行为。 这里要注意的是,在使用线程函数时,不能随意使用exit()退出函数来进行出错处理, 这是因为exit()函数的作用是使调用进程终止,而一个进程往往包含多个线程,因此,在使用exit()之后,该进程中的所有线程都会被退出,因此在线程中只能调用线程退出函数pthread_exit()而不是调用进程退出函数exit(),pthread_exit()函数原型如下:

void pthread_exit(void *retval);

参数说明

  • retval:如果retval不为空,则会将线程的退出值保存到retval中,如果不关心线程的退出值,形参为NULL即可。

4.2 pthread_join() 

一般情况下,进程中各个线程的运行是相互独立的,线程的终止并不会相互通知,也不会影响其他的线程, 终止的线程所占用的资源不会随着线程的终止而归还系统,而是仍为线程所在的进程持有,这是因为一个进程中的多个线程是共享数据段的。进程之间可以使用wait()系统调用来等待其他进程结束,线程也有类似的函数:

int pthread_join(pthread_t thread, void **rval_ptr);

如果某个线程想要等待另一个线程退出,并且获取它的退出值,那么就可以使pthread_join()函数完成,以阻塞的方式等待tid指定的线程结束,当函数返回时,被等待线程的资源将被收回,如果进程已经结束,那么该函数会立即返回。并且tid指定的线程必须是可结合状态的,该函数执行成功返回0,否则返回对应的错误代码。

参数说明

  • thread: 线程标识符,即线程ID,标识唯一线程。

  • rval_ptr: 用户定义的指针,用来存储被等待线程的返回值。

需要注意的是一个可结合状态的线程所占用的内存仅当有线程对其执行立pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止时,要么已被设为detached,要么使用pthread_join()来回收资源。

4.3 pthread_detach() 

默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。如果不关心线程的返回值,join是一种负担,这个时候,可以通过pthread_detach()函数将线程设置为detached,当线程退出时,自动释放线程资源,pthread_detach()函数如下:

int pthread_detach(pthread_t thread);

 参数说明

  • thread: 线程标识符,即线程ID,标识唯一线程。

  • 返回值:成功返回0,失败返回-1

joinable和detached是冲突的,一个线程不能既是joinable又是detached,可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:pthread_detach(pthread_self()),pthread_self()返回是所在线程的线程标识符

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>/*要执行的线程*/
void *test_thread(void *arg)
{int num = (int)arg; printf("This is test thread, arg is %d\n", num);sleep(5);/*退出线程*/pthread_exit(NULL);
}int main(void)
{pthread_t tid;void *thread_return;int arg = 520;int res;printf("start create thread\n");/*创建线程,线程执行test_thread函数*/res = pthread_create(&tid, NULL, test_thread, (void*)(arg));if(res != 0){printf("create thread fail\n");exit(res);}printf("create treads success\n");printf("waiting for threads to finish...\n");/*等待线程终止*/res = pthread_join(tid, &thread_return);if(res != 0){printf("thread exit fail\n");exit(res);}printf("thread exit ok\n");return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/321722.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux Shell】4. 数组

文章目录 【 1. 数组的定义 】【 2. 读取数组 】【 3. 关联数组 】【 4. 获取数组中的所有元素 】【 5. 获取数组的长度 】 数组中可以存放多个值。 Bash Shell 只支持一维数组&#xff08;不支持多维数组&#xff09;&#xff0c;初始化时不需要定义数组大小。与大部分编程语言…

1295. X的因子链(数论/求1~N的所以质因子)

题目&#xff1a; 1295. X的因子链 - AcWing题库 输入样例&#xff1a; 2 3 4 10 100输出样例&#xff1a; 1 1 1 1 2 1 2 2 4 6 思路&#xff1a; 代码&#xff1a; #include <cstdio> #include <cstring> #include <iostream> #include <algorithm…

内存的基础知识-第四十天

目录 什么是内存&#xff1f;内存的作用 常用的数量单位 指令的工作原理 思考 三种装入方式 绝对装入 可重定位装入&#xff08;静态重定位&#xff09; 动态运行时装入&#xff08;动态重定位&#xff09; 写程序到程序运行 编译和链接 链接的三种方式 本节思维导…

VS Code 如何调试Python文件

VS Code中有1,2,3处跟Run and Debug相关的按钮&#xff0c; 1 处&#xff1a;调试和运行就不多说了&#xff0c;Open Configurations就是打开workspace/.vscode下的lauch.json文件&#xff0c;而Add Configuration就是在lauch.json文件中添加当前运行Python文件的Configuratio…

Flyweight享元/共享模式(对象性能)

Flyweight 链接&#xff1a;享元模式实例代码 解析 目的 在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中&#xff0c;从而带来很高的运行时代价——主要指内存需求方面的代价。如何在避免大量细粒度对象问题的同时&#xff0c;让外部客户程序仍然…

前端开发加速器:十个VSCode插件精选

前端开发是一个不断发展的领域&#xff0c;随着技术的进步&#xff0c;工具也在不断更新。Visual Studio Code&#xff08;VSCode&#xff09;是前端开发者广泛使用的编辑器之一&#xff0c;得益于其强大的插件系统&#xff0c;可以帮助开发者提升工作效率。以下是十个对于前端…

1*2*3+3*4*5+...+99*100*101python,1加到100的程序算法python

大家好&#xff0c;本文将围绕python中123一直加到100程序怎么写展开说明&#xff0c;计算123456...100的值python是一个很多人都想弄明白的事情&#xff0c;想搞清楚计算1-23-45 … -100的值python需要先了解以下几个事情。 今天下午上python课的时候&#xff0c;老师留了一个…

广播及代码实现

广播&#xff08;Broadcast&#xff09;是一种网络通信方式&#xff0c;它允许一台设备向网络中的所有其他设备发送消息。广播通常用于在网络上传递一些信息&#xff0c;让所有设备都能接收并处理。在广播中&#xff0c;通信的目标是整个网络而不是特定的单个设备。 向子网中…

SpringCloud微服务安全之API审计日志功能实现

SpringCloud微服务安全之API审计日志功能实现 1.审计功能介绍2.记录的实体类设计3.保存审计记录到数据库的工具4.审计功能实现1.审计功能介绍 审计日志 定义:谁,在什么时间,干了什么事。位置:认证之后,授权之前。这样就知道是谁在访问,拒绝掉的访问也能被记录。如果放在认…

Text-to-SQL小白入门(十一)DAIL-SQL教你刷Spider榜单第一

论文概述 学习这篇Text2SQLLLM的benchmark论文前&#xff0c;可以先学习一下基础的Text2SQL知识。 可以参考GitHub项目&#xff1a;GitHub - eosphoros-ai/Awesome-Text2SQL: Curated tutorials and resources for Large Language Models, Text2SQL, Text2DSL、Text2API、Text2…

Transforer逐模块讲解

本文将按照transformer的结构图依次对各个模块进行讲解&#xff1a; 可以看一下模型的大致结构&#xff1a;主要有encode和decode两大部分组成&#xff0c;数据经过词embedding以及位置embedding得到encode的时输入数据 输入部分 embedding就是从原始数据中提取出单词或位置&…

腾讯云2024年最新优惠活动整理汇总

腾讯云作为国内领先的云计算服务提供商&#xff0c;一直致力于为用户提供优质、高效的服务。为了更好地满足用户需求&#xff0c;腾讯云经常推出各种优惠活动&#xff0c;本文将对腾讯云最新优惠活动进行整理汇总&#xff0c;帮助用户更好地了解和利用这些福利。 一、腾讯云新用…