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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

搜维尔科技:深入研究工作场所人体工程学中的动作捕捉

创造安全舒适的工作环境对于员工的健康和组织的整体成功至关重要。这就是为什么我们很高兴举办有关“在工作场所人体工程学中利用动作捕捉技术”的网络研讨会。 通过利用动作捕捉的力量&#xff0c;组织可以彻底改变工作场所人体工程学的方法。它使您能够根据客观的运动数据进行…

C++面向对象高级编程(侯捷)笔记2

侯捷C面向对象高级编程 本文是学习笔记&#xff0c;仅供个人学习使用&#xff0c;如有侵权&#xff0c;请联系删除。 如果你对C面向对象的组合、继承和委托不了解&#xff0c;对什么是拷贝构造、什么是拷贝赋值和析构不清楚&#xff0c;对类设计中的Adapter、pImpl、Template…

使用定时器setInterval,在Moment.js 时间格式化插件基础上完成当前时间持续动态变化

1、引入插件 npm install moment --save 2、js配置&#xff1a;当前需要使用的文件中直接引入 import moment from moment; 3、代码实现&#xff1a;定义一个变量进行回显 3.1、dom部分 <span> {{ timeData }} </span> 3.2、js代码 <script> import mo…

直播获奖(live)CSP2020

描述 NOI2130 即将举行。 为了增加观赏性&#xff0c;CCF 决定逐一评出每个选手的成绩&#xff0c;并直播即时的获奖分数线。 本次竞赛的获奖率为w&#xff0c;即当前排名前w 的选手的最低成绩就是即时的分数线。 更具体地&#xff0c;若当前已评出了 p 个选手的成绩&#…

GROUP_CONCAT报错解决

有如下表 其中awardee和awardee_unit都是保存的json类型的字符串, awardee是多个人员id, awardee_unit是部门的全路径 查询时要注意转换 需要将name拼接起来合并成一行,直接 GROUP_CONCAT 会报错 百度的大部分答案是修改数据库配置去掉严格模式,如果不方便修改数据库可以这样…

力扣刷题-二叉树-最大二叉树

654.最大二叉树 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下&#xff1a; 二叉树的根是数组中的最大元素。 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大…

Javascript学习之路:事件在页面加载和关闭时的执行顺序

&#x1f449;前言 在实际项目中&#xff0c;如果我想在打开浏览器页面加载时执行某些任务&#xff0c;比如获取当前页面的全部或部分数据&#xff0c;优先想到的事件是onload&#xff0c;当关闭页面时&#xff0c;我们也常常会使用onunload事件。但是这两个事件却有一定的局限…

图解算法数据结构-LeetBook-回溯01_机械累加器

请设计一个机械累加器&#xff0c;计算从 1、2… 一直累加到目标数值 target 的总和。注意这是一个只能进行加法操作的程序&#xff0c;不具备乘除、if-else、switch-case、for 循环、while 循环&#xff0c;及条件判断语句等高级功能。 注意&#xff1a;不能用等差数列求和公式…

多级缓存、OpenResty、Redis分布式缓存、进程缓存

目录标题 一、预期表现二、环境配置1、nginx环境2、OpenResty环境3、redis环境4、进程缓存环境 三 、主要编码工作3.1、OpenResty编码openresty/nginx/conf/nginx.conflualib/redis_common.lualualib/common.luanginx/lua/item.lua 3.2、进程缓存编码 四、结果分析4.1 初始无缓…

【KingbaseES】实现MySql函数len

原理&#xff1a;调用length函数返回到结果中 CREATE OR REPLACE FUNCTION len(input_text text) RETURNS integer AS $$ BEGIN RETURN length(input_text); END; $$ LANGUAGE plpgsql;结果如下&#xff1a;

Docker介绍、常用命令、项目部署

什么是Docker 简单说&#xff1a;Docker就是一个虚拟机&#xff0c;专业说&#xff1a;它是一个开源的容器平台。它和我们常用的VMware有很多相似的地方。 名词解释 镜像/images 由本体打包出来的文件。并不是文件本身&#xff0c;但是具有该文件的功能。举个不太贴切的例子&…

毛虫目标检测数据集VOC格式550张

毛虫&#xff0c;一种令人惊叹的生物&#xff0c;以其独特的外貌和习性&#xff0c;成为了自然界中的一道亮丽风景。 毛虫的外观非常特别&#xff0c;身体呈圆柱形&#xff0c;表面覆盖着许多细小的毛发&#xff0c;这使得它们在叶子上伪装得非常好。它们的头部有一对坚硬的颚…

【JavaSE】P1~P32 进制,字节,Java代码规范,常量,变量,运算符

目录 IDEA常用快键键和快捷用法一、需要复习的重难点概念1 进制、字节、DOS操作命令语句进制及转化字节DOS系统命令提示符 2 Java 常用代码规范3 常量及常量的打印4 变量数据类型及取值范围变量的概念和使用数据类型转换ASCII码表&#xff08;128种&#xff09;基本数据类型极其…

P1019 [NOIP2000 提高组] 单词接龙

网址如下&#xff1a;P1019 [NOIP2000 提高组] 单词接龙 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 很怪&#xff0c;不知道该说什么 我试了题目给的第一个测试点的输入&#xff0c;发现输出和测试点的一样&#xff0c;但是还是WA 不是很懂为什么 有没有大佬帮我看一下…

用 Python 抓取 bilibili 弹幕并分析!

01 实现思路 首先&#xff0c;利用哔哩哔哩的弹幕接口&#xff0c;把数据保存到本地。接着&#xff0c;对数据进行分词。最后&#xff0c;做了评论的可视化。 02 弹幕数据 平常我们在看视频时&#xff0c;弹幕是出现在视频上的。实际上在网页中&#xff0c;弹幕是被隐藏在源代码…

14.两数之和

题目 class Solution {public int[] twoSum(int[] nums, int target) {int[] ret {-1,-1};for(int i0;i<nums.length;i) {for(int ji1;j<nums.length;j) {if(nums[i] nums[j] target) {ret[0] i;ret[1] j;}}}return ret;} }

顶顶通呼叫中心中间件配置背景音乐(mod_cti基于FreeSWITCH)

介绍 配置外呼任务拨打时的背景音乐&#xff0c;一步步配置 一、配置队列外呼模板 打开ccadmin->队列呼叫模板->添加一条变量根据下方图片配置 二、上传音乐文件 后缀格式为.wav格式的声音文件&#xff0c;声音文件需要上传在这个目录&#xff1a;/ddt/fs/sounds/ct…

【编译原理】期末预习PPT前四章笔记II

看了看学校的ppt&#xff0c;记的比较随意O.o 因为我的考试范围里边没有简答所以概念什么的没怎么记 没有简答只有选择真是太好了嘿嘿嘿 目录 I. 概述&#xff08;好多字。。&#xff09; 一、高级语言的分类 1、体裁 2、执行方式 二、各种语言的执行方式 三、编译程序…

基于ssm的医院交互系统+vue论文

医院交互系统的设计与实现 摘要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。传统的医院交互信息管理模式&#xff0c;采用人工登记的方式保存相关数据&#xff0c;这种以人力为主的管理模…

CCNP课程实验-07-OSPF-Trouble-Shooting

目录 实验条件网络拓朴 环境配置开始排错错点1&#xff1a;R1-R2之间认证不匹配错误2&#xff1a;hello包的时间配置不匹配错误3&#xff1a;R2的e0/1接口区域配置不正确错误4&#xff1a;R4的e0/1接口没有配置进OSPF错误5&#xff1a;R2的区域1没有配置成特殊区域错误6&#x…