当前位置: 首页 > news >正文

Linux系统编程-线程、互斥锁与多线程模块的封装

目录

一. 线程

1.1 线程概念

1.2 线程与进程的区别

二. 线程相关函数接口

2.1 pthread_create

2.2 pthread_exit

2.3 pthread_join

2.3.1对比记忆

2.3.2 注意

2.3.3 示例代码

2.4 pthread_detach

三. 线程的互斥

3.1 线程的互斥机制

3.2 保护临界资源-互斥锁

3.2.1 pthread_mutex_init

3.2.2 pthread_mutex_lock/trylock

3.2.4 pthread_mutex_unlock(*mutex)

3.2.5 pthread_mutex_destroy(*mutex)

四. 多线程模块的封装

一. 线程

1.1 线程概念

线程:线程是轻量级进程(LWP)

1.2 线程与进程的区别

进程线程
正在执行的程序

轻量级进程

操作系统资源分配的最小单位操作系统任务调度的最小单位
资源空间消耗大,0~4G虚拟内存地址空间资源空间消耗小,栈区独立,其他空间共享
进程的效率低线程效率高
进程安全性高线程安全性低
进程间通信复杂,需要用到IPC机制线程间通信方便,由于共享数据区,使用全局变量即可
在相同资源的平台下,多进程的并发量要少于多线程的并发量

二. 线程相关函数接口

2.1 pthread_create

作用:会在调用进程内启动一个新的线程

参数:*thread:保存新线程的id

*attr:线程属性

*start_routine:回调函数,线程的执行函数

*arg:给回调函数的参数

返回值:成功返回0 失败返回一个错误号

2.2 pthread_exit

作用:pthread_exit()函数会终止调用该函数的线程,并通过retval参数返回一个值;而这个值则

可供同一进程中调用pthread_join()的其他线程使用。

当一个线程终止时,进程共享资源(例如互斥锁、条件变量、信号量和文件描述符)不会被

释放

当进程中的最后一个线程终止后,该进程便会像调用exit()并设置退出状态为零那样结束;

因此,进程共享资源会被释放。

参数:*retval:退出值

返回值:

2.3 pthread_join

作用:阻塞等待线程退出

pthread_join()函数会等待由thread参数指定的线程完成其执行过程。如果该线程已经结

束执行,那么pthread_join()将立即返回。这个由thread指定的线程必须是非分离状态

如果 retval 不为 NULL,则 pthread_join() 会将目标线程的退出状态复制到由 retval 所指向

的位置。如果目标线程被取消(pthread_cancel),则会在由 retval 所指向的位置放置PTHREAD_CANCELED (-1)标志。

参数:thread:指定线程 **retval:保存线程退出状态

返回值:成功返回0 失败返回一个错误号

2.3.1对比记忆

线程与进程在接收返回值时的对比:

进程线程

进程在退出时可使用:return 0;return -1;exit(1);

exit(0);_exit(0);_Exit(0)等

可以看到进程的退出状态都是整型

线程的退出可使用:return NULL;pthread_exit(NULL)等

可以看到线程的退出状态都是指针类型,另外,在pthread_create创建线程时,那个回调函数的返回类型就是void*类型,这就规定了线程的执行逻辑的返回值类型是void*指针类型

在获取进程的退出状态时,使用函数:wait或者waitpid,其中有一个参数int *wstatus来保存进程的退出状态,然后使用各种宏来获取进程的退出值等

由于进程的退出状态是int,故用int*来接收

获取线程的退出状态时,使用pthread_join,第二个参数void **retval就是用来保存线程的退出状态的

由于线程的退出状态是void*,故使用void**来接收

2.3.2 注意

注意:使用pthread_exit不可返回栈区空间的地址,因为线程结束,栈区空间释放,会出现段错误。

以下指针可以通过函数返回:全局变量的地址

static修饰的局部变量的地址

堆区申请的未被释放的空间的地址

字符串常量的地址

通过函数传参传过来的地址

2.3.3 示例代码

int num_g = 66; void *th_task1(void *arg) { int cnt = 5; while(cnt--){ printf("---thread 1 :%ld,pid: %d\n",pthread_self(),getpid()); sleep(1); } //return &num_g; return (void*)666; } void *th_task2(void *arg) { while(1){ printf("---thread 2 :%ld, pid : %d\n",pthread_self(), getpid()); sleep(1); } return NULL; } int main() { int ret; void *retval; pthread_t tid1, tid2; ret = pthread_create(&tid1, NULL,th_task1,NULL); if(ret != 0){ fprintf(stderr,"pthread_create error:%s\n",strerror(ret)); exit(1); } ret = pthread_create(&tid2, NULL,th_task2,NULL); if(ret != 0){ fprintf(stderr,"pthread_create error:%s\n",strerror(ret)); exit(1); } printf("---thread main :%ld, pid : %d\n",pthread_self(), getpid()); printf("main:%ld,%ld\n",tid1,tid2); pthread_join(tid1, &retval); //printf("---pthread join finish:%ld,retval is %d\n",tid1,*(int*)retval); printf("---pthread join finish:%ld,retval is %ld\n",tid1,(long)retval); /这种情况下最好将接收的状态值强转为long型,因为64位系统下long与指针都是8位 pthread_join(tid2, NULL); return 0; }

结果:

2.4 pthread_detach

作用:pthread_detach() 函数将由 thread 标识的线程标记为已分离状态。当一个已分离的线程终止时,其资源会自动返回给系统,无需其他线程与已终止的线程进行连接。试图分离一个已经分离的线程会导致未定义的行为。

参数:thread指定的线程

返回值:成功返回0 失败返回一个错误号

分离线程:不需要回收的线程,当线程结束时可以被操作系统自动回收其资源,类似孤儿进程。

非分离线程:可以被其他回收或者结束的线程,默认属性为非分离线程

示例代码:

void *th_task1(void *arg) { return NULL; } int main() { pthread_t tid; int num = 0; while(1){ num++; printf("num is %d\n",num); pthread_create(&tid, NULL,th_task1,NULL); pthread_detach(tid); } return 0; }

三. 线程的互斥

3.1 线程的互斥机制

多线程在访问临界资源时存在资源竞争!
临界资源:多个线程可以同时访问的资源,如全局变量,共享内存等
线程互斥机制:让多个线程在访问临界资源时,具有排他访问的特性

3.2 保护临界资源-互斥锁

互斥锁:

使用流程:创建锁->初始化锁->加锁->访问临界资源->解锁->销毁锁


3.2.1 pthread_mutex_init


功能:初始化互斥锁
参数:互斥锁对象
锁的属性
返回值:成功返回0 失败返回错误码


3.2.2 pthread_mutex_lock/trylock


功能:pthread_mutex_lock:以阻塞方式等待锁
pthread_mutex_trylock:以非阻塞方式等待锁
返回值:成功返回0 失败返回错误号


3.2.4 pthread_mutex_unlock(*mutex)


功能:解锁

返回值:成功返回0 失败返回错误号


3.2.5 pthread_mutex_destroy(*mutex)


功能:销毁锁

示例代码:模拟atm取钱程序:

pthread_mutex_t mutex; int atm = 3; void * pfun(void *arg) { while(1){ //访问atm公共资源,先加锁 pthread_mutex_lock(&mutex); if(atm > 0){ printf("atm = %d\n",atm); atm--; printf("--[%ld]:draw money---\n",pthread_self()); pthread_mutex_unlock(&mutex);//解锁后,取钱 sleep(rand() % 3 + 1); pthread_mutex_lock(&mutex);//加锁,再访问atm atm++; pthread_mutex_unlock(&mutex);//解锁 return NULL; } else{ pthread_mutex_unlock(&mutex); } } } int main() { pthread_t tid[10]; int ret; int i = 0; srand(time(NULL)); pthread_mutex_init(&mutex, NULL); //10个线程去atm取钱 for(i = 0; i < 10; i++){ ret = pthread_create(&tid[i], NULL, pfun, NULL); if(ret != 0){ fprintf(stderr,"pthread_create error:%s\n",strerror(ret)); exit(1); } } for(i = 0; i < 10; i++){ ret = pthread_join(tid[i], NULL); if(ret != 0){ fprintf(stderr,"pthread_join error:%s\n",strerror(ret)); exit(1); } } pthread_mutex_destroy(&mutex); printf("main:draw money finish!\n"); return 0; }

3.3 死锁

死锁:指的是在多线程环境中,每个执行流(线程)都有未释放的资源,且互相请求对方未释放资
源,从而导致陷入永久等待状态的情况。
现象:现象1:忘记释放锁
现象2:重复加锁
现象3:多线程多锁,抢占锁资源不当

产生死锁的四个必要条件(四条全中 = 死锁,破掉任意一条 = 解除)
(1)互斥条件:一个资源同一时刻只能被一个任务占用(一个执行流获取锁后,其它执行流不能再获取该锁)。
(2)请求与保持条件:已经拿着一部分资源不释放,同时又去申请新的资源(执行流本身使用着一把锁并不释放,还在请求别的锁)。
(3)不剥夺条件:资源只能由持有者主动释放,系统/其他任务不能强行抢过来(A执行流拿着锁,其它执行流不能释放)。
(4)循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对方想要的锁,并且各执行流还去请求对方的锁)。


解决方法:
1.锁一定要成对出现
2.使线程的加解锁顺序一致
3.破坏环路等待条件
使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的资源

四. 多线程模块的封装

tasks.c

/创建多个线程 int creat_thread_tasks(Task_t *tasks, int len) { for(int i = 0; i < len; i++){ int ret = pthread_create(&(tasks[i].tid), NULL, tasks[i].pfun, NULL); if(ret != 0 ){ fprintf(stderr, "pthread_create error:%s\n",strerror(ret)); return -1; } } return 0; } /销毁多个线程 void destory_thread_tasks(Task_t *tasks, int len) { for(int i = 0; i < len; i++){ int ret = pthread_join(tasks[i].tid, NULL); } }

tasks.h

#ifndef __TASKS_H__ #define __TASKS_H__ #include<pthread.h> typedef struct task{ pthread_t tid; void *(*pfun)(void*); }Task_t; extern int creat_thread_tasks(Task_t *tasks, int len); extern void destory_thread_tasks(Task_t *tasks, int len); #endif

main.c

#include<stdio.h> #include "tasks.h" #include<unistd.h> #include<stdlib.h> //#include<pthread.h> void *main_ctl_task(void *arg) { while(1){ printf("[%ld]:main_ctl_task is running\n",pthread_self()); sleep(1); } } void *get_usr_cmd_task(void *arg) { while(1){ printf("[%ld]:get_usr_cmd_task is running\n",pthread_self()); sleep(1); } } void *exec_usr_cmd_task(void *arg) { while(1){ printf("[%ld]:exec_usr_cmd_task is running\n",pthread_self()); sleep(1); } } void * get_pic_task(void *arg) { while(1){ printf("[%ld]:get_pic_task is running\n",pthread_self()); sleep(1); } } void *send_pic_task(void *arg) { while(1){ printf("[%ld]:send_pic_task is running\n",pthread_self()); sleep(1); } } /Linux支持的部分初始化方式 Task_t tasks[] = { { .pfun = main_ctl_task, }, { .pfun = get_usr_cmd_task, }, { .pfun = exec_usr_cmd_task, }, { .pfun = get_pic_task, }, { .pfun = send_pic_task, }, }; int main() { int ret = creat_thread_tasks(tasks,sizeof(tasks)/sizeof(tasks[0])); if(ret == -1){ fprintf(stderr, "creat_thread_tasks error\n"); exit(1); } destory_thread_tasks(tasks,sizeof(tasks)/sizeof(tasks[0])); return 0; }
http://www.zskr.cn/news/1498163.html

相关文章:

  • 避坑指南:VS Code verilog-format插件配置最常见的3个错误(及正确设置方法)
  • 配电网通信技术全解析:架构方案与应用
  • 2026年重庆市健身塑形训练营哪家好 重庆SGO封闭式健身训练营 联系电话:19122466397 - 速递信息
  • 避坑指南:MMSegmentation训练自定义数据集时,这些配置项千万别乱改(基于UperNet消融实验)
  • 别再只知EMD了!VMD、SSA、ITD算法选型指南:从原理到场景的深度解析
  • 铲屎官必看!猫咪掉毛自救指南 - 品牌测评鉴赏家
  • 广州无证书钻石别扔!添价收免费检测估价,不压价秒到账 - 薛定谔的梨花猫
  • 上海防水堵漏公司对比:晶亮 VS 传统公司,3 大维度见真章 - 热点速览
  • 龙虾很强,但企业需要「帝王蟹」!
  • 别再手动复制了!用RStudio的sink()函数自动记录你的完整分析日志
  • KAPT生成代码的集成与管理
  • 海悟参编液冷不锈钢管路团标 完善数据中心液冷温控标准体系
  • 无锡装修公司真实口碑汇总:综合实力与客户认可度双优装企解析 - 装修新知
  • 创梦汤锅学习日记day28
  • 2026年上海徐汇区寻宠秘籍:这家宠物店如何成为找猫高手?
  • 2026 安丘厨卫屋面地下室漏水瓷砖空鼓测评:吉修匠 99.8 分五星榜首 - 吉修匠
  • 厦门钻石上门回收哪家安全?本地盘点钻戒回收隐患避坑 - 开心测评
  • 2026北京卡地亚回收避坑指南!看懂套路、精准估价、稳妥出手 - 薛定谔的梨花猫
  • 利用Python开发自动化脚本:提高工作效率
  • 2026上海名表回收实测|正规行情避坑,合扬凭硬核实力成首选 - 开心测评
  • SonarScanner 在 Windows 命令行下的实战:从单个项目扫描到集成 Jenkins 自动化
  • 2026杭州工装装修公司靠谱榜单盘点,办公室、商铺、酒店装修优选参考 - 装修新知
  • 2026年安徽省淮南市中考落榜怎么办?还可以上什么公办学校?官网最新发布 - 小张zc
  • 2026夏至海报设计素材哪里找?十款优质图片网站实测测评 - 品牌2026
  • 20个超实用Python技巧,告别冗余代码,新手也能写出高质量代码
  • 餐饮竹木灯饰定制全指南:性价比与贴心服务核心维度 - 奔跑123
  • 百达翡丽回收|2026 西安 5 家门店实测,顶级名表怎么卖不被坑 - 奢侈品回收测评
  • 2026深圳黄金回收怎么选?五大正规门店,适配不同变现需求 - 奢侈品回收测评
  • 河南铝单板厂家技术实力拆解:从产品到服务的硬核标准 - 奔跑123
  • QT5.14.2安装后第一件事:手把手教你配置项目目录与创建纯C控制台应用