Linux系统编程(十一)线程、线程控制

线程

一、线程概念:

线程概念

  • ps -eLf 查看线程号(cpu 执行的最小单位)

ps_Lf

最小执行单元

二、Linux内核线程实现原理

线程实现原理

三、三级映射(三级页表)

进程PCB-->页面(可看成数组,首地址位于PCB中)--》页表--》页目录(物理页表)--》内存单元参考:《Linux内核源代码情景分析》 ---毛德操

三级映射

线程实现原理-2

四、线程共享资源

线程共享资源

五、线程非共享资源

线程非共享资源

六、线程优缺点

线程优缺点

线程控制原语

一、pthread_self函数

线程ID:用来表示线程身份的id号

线程号(LWP):内核用来将线程作为进程看待

pthread_t pthread_self(void);	获取线程id。 线程id是在进程地址空间内部,用来标识线程身份的id号。返回值:本线程id(unsigned long类型)

二、pthread_create函数

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*start_rountn)(void *), void *arg); //创建子线程。参1:传出参数,表新创建的子线程 id参2:线程属性。传NULL表使用默认属性。(线程状态:大小、优先级等)参3:子线程回调函数。创建成功,ptherad_create函数返回时,该函数会被自动调用。参4:参3的参数。没有的话,传NULL返回值:成功:0失败:返回errno编译时需要引入动态库 -lpthread

示例1:循环创建N个子线程:

void *tfn(void* arg)
{int i = (int)arg;sleep(i);if(i == 2)pthread_exit(NULL);//退出当前线程printf("--%dth pthread,pid=%d,tid=%lu\n", i+1,getpid(),pthread_self());return NULL;
}
int main(int argc, char* argv[])
{int i;int ret;pthread_t tid;for(i=0; i<5; i++){ret = pthread_create(&tid,NULL,tfn,(void *)i);// 栈是不共享的,将 int 类型 i, 强转成 void *, 传参采用值传递if(ret != 0){perror("pthread_create error");exit(1);}}sleep(i);printf("main,pid=%d,tid=%lu\n", getpid(),pthread_self());return 0;
}

循环创建子线程-错误分析

示例2:线程共享全局变量

线程默认共享数据段、代码段等地址空间,常用的是全局变量。

进程不共享全局变量,只能借助mmap

int var = 100;
void* ftn(void* arg)
{printf("child,pid=%d, tid=%lu, var=%d\n", getpid(),pthread_self(),var);var = 200;pthread_exit(NULL);
}
int main(int argc, char* argv[])
{pthread_t tid;int ret;ret = pthread_create(&tid,NULL,ftn,NULL);if(ret!=0){perror("pthread_create error");exit(1);}sleep(1);printf("main,var = %d\n",var);//全局变量共享return 0;
}

三、pthread_exit函数

void pthread_exit(void *retval);  退出当前线程。retval:退出值:返回线程,通常传NULL。三种退出的区别:exit();	退出当前进程。//exit(0)表示正常退出return: 返回到函数调用者那里去。pthread_exit(): 退出当前线程。注意:(1)多线程环境中,应尽量不使用exit函数,应该使用pthread_exit函数。(2)其它线程未结束,主线程不能return或exit。(3)在子线程中使用pthread_exit或return返回的指针所指向的内存单元必须是全局的或者malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时,线程函数已经退出了。

四、pthread_join函数

int pthread_join(pthread_t thread, void **retval); //阻塞等待并回收线程,获取线程退出状态。thread: 待回收的线程idretval:传出参数。 回收的子线程的return退出值。线程异常借助,值为 -1。返回值:成功:0失败:errno

示例1:pthread_join函数的使用及注意事项

//pthread_join.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>struct thrd {int var;char str[256];
};void sys_err(const char *str)
{perror(str);exit(1);
}
/*
void *tfn(void *arg)
{struct thrd *tval;tval = malloc(sizeof(tval));//1、在堆区创建变量tval->var = 100;strcpy(tval->str, "hello thread");return (void *)tval;
}
*/
/*
void *tfn(void *arg)
{struct thrd tval;              //2、局部变量地址,不可做返回值tval.var = 100;strcpy(tval.str, "hello thread");return (void *)&tval;
}
*/ 
void *tfn(void *arg)
{struct thrd *tval = (struct thrd *)arg;tval->var = 100;strcpy(tval->str, "hello thread");return (void *)tval;
}int main(int argc, char *argv[])
{pthread_t tid;struct thrd arg;//3、可以在main函数创建变量struct thrd *retval;int ret = pthread_create(&tid, NULL, tfn, (void *)&arg);if (ret != 0)sys_err("pthread_create error");//int pthread_join(pthread_t thread, void **retval);ret = pthread_join(tid, (void **)&retval);if (ret != 0)sys_err("pthread_join error");printf("child thread exit with var= %d, str= %s\n", retval->var, retval->str);pthread_exit(NULL);}

示例2:使用pthread_join函数将循环创建的多个子线程回收(使用数组)

//pthrd_loop_join.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>int var = 100;void *tfn(void *arg)
{int i;i = (int)arg;sleep(i);if (i == 1) {var = 333;printf("var = %d\n", var);return (void *)var;} else  if (i == 3) {var = 777;printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i+1, pthread_self(), var);pthread_exit((void *)var);} else  {printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i+1, pthread_self(), var);pthread_exit((void *)var);}return NULL;
}int main(void)
{pthread_t tid[5];int i;int *ret[5];  for (i = 0; i < 5; i++)pthread_create(&tid[i], NULL, tfn, (void *)i);for (i = 0; i < 5; i++) {pthread_join(tid[i], (void **)&ret[i]);printf("-------%d 's ret = %d\n", i, (int)ret[i]);}printf("I'm main pthread tid = %lu\t var = %d\n", pthread_self(), var);sleep(i);return 0;
}

五、pthread_detach函数

int pthread_detach(pthread_t thread);		设置线程分离thread: 待分离的线程id返回值:成功:0失败:errno	--- 线程报错直接返回错误号,不能使用perror,需使用strerror函数。(1)线程分离状态:指定该状态,线程主动与主线程断开关系。线程结束后,其退出状态不由其它线程获取,而直接自己自动释放。网络、多线程服务器常用。(2)进程若有该机制,将不会产生僵尸进程。僵尸进程的产生主要由于进程死后,大部分资源被释放,一点残留资源仍存于系统重,导致内核认为该进程仍存在。(3)也可使用`pthread_create`函数的参2(线程属性)来设置线程分离。(4)不能对一个已经处于detach状态的线程调用`pthread_join`,这样的调用将返回`EINVAL`无效错误。(5)使用pthread_join回收失败,说明分离成功

示例:pthread_detach的使用

//pthread_detach.c
void *tfn(void *arg)
{printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());return NULL;
}int main(int argc, char *argv[])
{pthread_t tid;int ret = pthread_create(&tid, NULL, tfn, NULL);if (ret != 0) {fprintf(stderr, "pthread_create error: %s\n", strerror(ret));exit(1);}ret = pthread_detach(tid);              // 设置线程分离` 线程终止,会自动清理pcb,无需回收if (ret != 0) {fprintf(stderr, "pthread_detach error: %s\n", strerror(ret));exit(1);}sleep(1);ret = pthread_join(tid, NULL);if (ret != 0) {fprintf(stderr, "pthread_join error: %s\n", strerror(ret));exit(1);}printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());pthread_exit((void *)0);
}

六、pthread_cancel函数

int pthread_cancel(pthread_t thread);//杀死一个线程。thread: 待杀死的线程id返回值:成功:0,成功被 pthread_cancel()杀死的线程,无法使用pthread_join回收,返回 #define PTHREAD_CANDELED ((void *)-1)(表示非正常死亡)。使用pthead_join 回收失败:errno注意:(1)线程的取消并不是实时的,而有一定的延时。需要等待线程到达某个取消点(检查点)。如果,子线程没有到达取消点, 那么 pthread_cancel 无效。(2)** 可粗略认为一个系统调用(进入内核)即为一个取消点。(3)我们可以在程序中,手动添加一个取消点。使用 pthread_testcancel();

示例:pthread_cancel的使用

//pthread_cancel.c
void* ftn(void* arg)
{while(1){printf("child,tid=%lu\n", pthread_self());sleep(1);pthread_testcancel();//可以使用这个函数进行测试,进内核}
}int main(int argc, char* argv[])
{pthread_t tid;int ret;ret = pthread_create(&tid,NULL,ftn,NULL);if(ret != 0){fprintf(stderr,"pthread_create err:%s",strerror(ret));}sleep(5);ret = pthread_cancel(tid);//需要到达一个取消点才能杀死线程,进内核if(ret != 0){fprintf(stderr, "pthread_cancel err:%s", strerror(ret));}pthread_join(tid,&tret);printf("pthread_join,tret=%d\n",(int)tret);//cancel后,无法使用join回收,返回-1return 0;
}

七、检查错误返回

检查出错返回:  线程中,只能使用strerror函数fprintf(stderr, "xxx error: %s\n", strerror(ret));

八、进程和线程控制原语对比

线程控制原语进程控制原语用途
pthread_create()fork();创建
pthread_self()getpid();获取
pthread_exit()exit()/return退出
pthread_join()wait()/waitpid()回收
pthread_cancel()kill()杀死
pthread_detach()分离

九、设置线程属性(通过函数设置属性)

线程属性

pthread_attr_t attr  	创建一个线程属性结构体变量pthread_attr_init(pthread_attr_t *attr);	初始化线程属性pthread_attr_getdetachstate(pthread_attr_t *attr, int detachstate);//获取线程属性pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);//设置线程属性detachstate:PTHREAD_CREATE_DETACHED--分离线程PTHREAD_CREATE_JOINABLE--非分离线程(默认)pthread_create(&tid, &attr, tfn, NULL); 借助修改后的 设置线程属性 创建为分离态的新线程pthread_attr_destroy(pthread_attr_t *attr);	销毁线程属性

示例:pthread_create中分离属性的使用

//pthrd_attr_detach.c
void *tfn(void *arg)
{printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());return NULL;
}int main(int argc, char *argv[])
{pthread_t tid;pthread_attr_t attr;int ret = pthread_attr_init(&attr);if (ret != 0) {fprintf(stderr, "attr_init error:%s\n", strerror(ret));exit(1);}ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);      // 设置线程属性为 分离属性if (ret != 0) {fprintf(stderr, "attr_setdetachstate error:%s\n", strerror(ret));exit(1);}ret = pthread_create(&tid, &attr, tfn, NULL);if (ret != 0) {perror("pthread_create error");}ret = pthread_attr_destroy(&attr);if (ret != 0) {fprintf(stderr, "attr_destroy error:%s\n", strerror(ret));exit(1);}ret = pthread_join(tid, NULL);if (ret != 0) {fprintf(stderr, "pthread_join error:%s\n", strerror(ret));exit(1);}printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());pthread_exit((void *)0);
}

线程同步问题: 如果设置一个线程为分离线程,而这个线程运行又非常快,它可能在pthread_create函数返回之前就终止了;它终止之后可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,设置一段等待时间,是在多线程编程里常用的方法。但注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

十、线程使用注意事项

线程使用注意事项
多线程fork进程

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

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

相关文章

计算机系统基础笔记(10)——浮点数

前言 结合第四篇观看 效果更好^^ 数值型分为两类 定点数 整数小数 浮点数 注意&#xff1a;小数点不用占用二进制位 第一部分 编码 1.小数的二进制表示 位置计数法&#xff1a;小数点左边的权重是正指数&#xff0c;右边的数是负指数&#xff0c;如图 由此可知&#xff0…

MySQL数据库(二)和java复习

一.MySQL数据库学习(二) (一).DQL查询数据 DQL&#xff08;Data Query Language&#xff09;是用于从数据库中检索数据的语言。常见的 DQL 语句包括 SELECT、FROM、WHERE、GROUP BY、HAVING 和 ORDER BY 等关键字&#xff0c;用于指定要检索的数据、数据源、过滤条件、分组方…

格式化后硬盘数据能恢复吗?硬盘数据恢复这样做!

硬盘是电脑中必备的数据存储设备&#xff0c;另外还有移动硬盘。移动硬盘存储空间非常大、性价比高、便于携带&#xff0c;给我们带来和很多便利。但是和其他存储设备一样&#xff0c;各种硬盘也会出现各种问题&#xff0c;比如常见的格式化硬盘导致数据丢失的问题。 怎么样恢复…

PHP 寿光蔬菜大棚宣传平台-计算机毕业设计源码88288

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于寿光蔬菜大棚宣传平台当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了寿光蔬菜大棚宣传平台&#xff0c;它彻底…

BPF:BCC(BPF Compiler Collection)工具集认知

写在前面 博文内容为 《BPF Performance Tools》 读书笔记整理内容涉及 BCC 工具整体介绍理解不足小伙伴帮忙指正 &#x1f603;,生活加油 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前的风景已经和从前不一样了。——村…

从河流到空气,BL340工控机助力全面环保监测网络构建

在环保监测领域&#xff0c;智能化、高效率的监测手段正逐步成为守护绿水青山的新常态。其中&#xff0c;ARMxy工业计算机BL340凭借其强大的处理能力、高度的灵活性以及广泛的兼容性&#xff0c;在水质监测站、空气质量检测、噪音污染监控等多个环保应用场景中脱颖而出&#xf…

【Linux】进程3——PID/PPID,父进程,子进程

在讲父子进程之前&#xff0c;我们接着上面那篇继续讲 1.查看进程 mycode.c makefile 我们在zs_108直接编译mycode.c&#xff0c;直接运行&#xff0c;然后我们转换另一个账号来查看这个进程 我们可以通过ps指令来查看进程 我们就会好奇了&#xff0c;第二行是什么&#xff…

Linux shell编程学习笔记58:cat /proc/mem 获取系统内存信息

0 前言 在开展系统安全检查的过程中&#xff0c;除了收集cpu信息&#xff0c;我们还需要收集内存信息。在Linux中&#xff0c;获取内存信息的命令很多&#xff0c;这里我们着重研究 cat /proc/mem命令。 1 cat /proc/mem命令 /proc/meminfo 文件提供了有关系统内存的使用情况…

280 基于matlab的摇号系统GUI界面仿真MATLAB程序

基于matlab的摇号系统GUI界面仿真MATLAB程序&#xff0c;输入总数量及摇号需求&#xff0c;进行随机性摇号&#xff0c;并对摇取的号码进行双重随机性数据检测&#xff0c;确定是否符合要求。程序已调通&#xff0c;可直接运行。 280 GUI人机交互 摇号系统GUI界面仿真 - 小红书…

技术前沿 |【大模型InstructBLIP进行指令微调】

大模型InstructBLIP进行指令微调 一、引言二、InstructBLIP模型介绍三、指令微调训练通用视觉语言模型的应用潜力四、InstructBLIP的指令微调训练步骤五、实验结果与讨论六、结论与展望 一、引言 随着人工智能技术的快速发展&#xff0c;视觉语言模型&#xff08;Vision-Langu…

使用SourceTree切换不同的托管平台

背景&#xff1a;sourcetree一开始绑定了gitee&#xff0c;想拉取github的项目时拉取不了 原因&#xff1a;git绑定的账号&#xff08;邮箱&#xff09;、密码不一致 解决办法&#xff1a; 重新设置账号密码 在windows种可找到下面的文件夹&#xff0c;进行删除 C:\Users\US…

5.1 实体完整性

一个表只能有一个主键约束&#xff0c;且主键约束不能取空值。 通过unique约束定义唯一性&#xff0c;为了保证一个表非主键列不输入重复值&#xff0c;可在该列定义unique约束。 primary key约束与unique约束主要区别如下。 (1)一个表只能创建一个primary key约束&#xff0…

让GNSSRTK不再难【第一天】

第1讲 GNSS系统组成以及应用 北斗导航科普动画_哔哩哔哩_bilibili 1.1 GNSS系统 1.1.1 基本概念 全球卫星导航系统&#xff08;Global Navigation Satellite System, GNSS&#xff09;&#xff0c;是能在地球表面或近地空间的任何地点为用户提供全天候的三维坐标、速度以及…

STM32-电灯,仿真

目录 前言: 一. 配置vscode 二. 新创建软件工程 三. 仿真 1.新建工程想到,选择名称和路径 2.从选中的模板创建原理图 3.不创建PCB布版设计 4.选择没有固件项目 5.完成 四.源码 五. 运行效果 六. 总结 前言: 这篇主要是配置vscode和创建仿真,和点灯的完整代码,欢迎大…

在Windows上用Llama Factory微调Llama 3的基本操作

这篇博客参考了一些文章&#xff0c;例如&#xff1a;教程&#xff1a;利用LLaMA_Factory微调llama3:8b大模型_llama3模型微调保存-CSDN博客 也可以参考Llama Factory的Readme&#xff1a;GitHub - hiyouga/LLaMA-Factory: Unify Efficient Fine-Tuning of 100 LLMsUnify Effi…

美琳莱卡:创新消费模式引领新零售时代

公司成立时间与定位 美琳莱卡自创立之初,便以独特的视角和前瞻性的战略定位,立足于消费市场的变革前沿。公司成立于2024年,正值全球数字化浪潮蓬勃兴起,消费升级趋势日益明显之际。美琳莱卡敏锐地捕捉到这一时代机遇,将自身定位为创新消费模式的引领者,致力于通过线上线下高度…

攻防演练之-网络集结号

每一次的网络安全攻防演练都是各个安全厂商期待的网络安全盛会&#xff0c;因为目前的安全生态导致了只有在网络安全攻防演练期间&#xff0c;网络安全的价值才会走向台前&#xff0c;收到相关方的重视。虽然每一次都会由于各种原因不能如期举行&#xff0c;但是这一次的推迟总…

idea最新专业版安装+maven配置教程!

本教程适用于 J B 全系列产品&#xff0c;包括 Pycharm、IDEA、WebStorm、Phpstorm、Datagrip、RubyMine、CLion、AppCode 等。 &#xff08;直接复制&#xff0c;拿走不谢&#xff09; 9H1390TRAK-eyJsaWNlbnNlSWQiOiI5SDEzOTBUUkFLIiwibGljZW5zZWVOYW1lIjoi5rC45LmF5rA5rS7I…

MySQL之查询性能优化(七)

查询性能优化 排序优化 无论如何排序都是一个成本很高的操作&#xff0c;所以从性能角度考虑&#xff0c;应尽可能避免排序或者尽可能避免对大量数据进行排序。前面已经提到了&#xff0c;当不能使用索引生成排序结果的时候&#xff0c;MySQL需要自己进行排序&#xff0c;如果…

【传知代码】上下位关系自动检测方法(论文复现)

前言&#xff1a;在信息爆炸的时代&#xff0c;我们每天都沉浸在海量的数据和信息中。随着互联网技术的飞速发展&#xff0c;如何从这些信息中准确、高效地提取出有用的知识&#xff0c;成为了当下研究的热点。其中&#xff0c;上下位关系&#xff08;也称为层级关系或种属关系…