news 2026/10/23 10:25:53

30天速通Linux 第八章多线程编程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
30天速通Linux 第八章多线程编程

学习资料https://pan.baidu.com/s/1NZpC4NyH0OHGOPPbO9J10g?pwd=h9m2 提取码: h9m2

一. 多线程概念

  • 进程是资源分配单位,线程是 CPU 调度单位,又叫轻量级进程。
  • 同一进程的线程共享地址空间、文件描述符、信号处理等,切换代价远小于进程。
  • Linux 线程是用户级线程,遵循 POSIX 接口pthread:<pthread.h>,编译加-pthread。

为什么用多线程:开销小响应快 / 共享数据通信方便 / 耗时操作放新线程不影响主线程响应 / 多 CPU 并行。

⚠ 共享内存带来便利也带来风险:访问共享资源必须做同步与互斥。

二. 线程编程

2.1 创建pthread_create

intpthread_create(pthread_t*tid,constpthread_attr_t*attr,void*(*start_rtn)(void*),void*arg);// 成功 0,失败返回错误码(别用 errno)
  • arg传参:传结构体指针即可(线程内强转回来)。
  • 进程的所有信息(代码、全局/堆内存、栈、fd)对所有线程共享。
typedefstructstudent{intage;charname[20];}STU;void*create(void*arg){STU*temp=(STU*)arg;printf("STU age is %d, name is %s\n",temp->age,temp->name);}STU*stu=malloc(sizeof(STU));stu->age=20;strcpy(stu->name,"abcdefg");pthread_create(&tidp,NULL,create,(void*)stu);pthread_join(tidp,NULL);

2.2 终止pthread_exit

voidpthread_exit(void*ret_val);// ret_val 交给 pthread_join
  • 正常终止:pthread_exit或线程函数return。
  • ⚠ 线程里调exit()会结束整个进程!
pthread_exitexit
影响只结束当前线程整个进程结束

清理函数(先进后出,只对pthread_exit有效)

voidpthread_cleanup_push(void(*rtn)(void*),void*arg);voidpthread_cleanup_pop(intexecute);// 0 = 不执行,非 0 = 执行
pthread_cleanup_push(clean_1,"thread first handler");pthread_cleanup_push(clean_1,"thread second hadler");if(i==1)pthread_exit(NULL);// 执行清理,顺序是 second → first(后进先出)elseexit(0);// 对 exit 无效pthread_cleanup_pop(0);pthread_cleanup_pop(0);

两个调用必须配套;线程正常return或exit时清理函数不会执行。

2.3 等待pthread_join

intpthread_join(pthread_tthread,void**thread_return);// 挂起自己等指定线程结束
  • 只能等待同一进程的线程;⚠ 一个线程不能被多个线程 join。
void*t2_exe(void*arg){pthread_join((pthread_t)arg,NULL);// 先把线程1 join 掉printf("The second thread:\n");// 线程1结束后才打印 6~10}pthread_create(&pid1,NULL,t1_exe,NULL);pthread_create(&pid2,NULL,t2_exe,(void*)pid1);

2.4 获取自身线程号

pthread_tpthread_self(void);// 同一进程内 pid 相同,线程 ID 不同

三. 同步与互斥

三种机制:互斥量、信号量、条件变量。

3.1 互斥量 mutex

相当于一把锁,保证:①原子性(临界区要么全做要么不做)②唯一性(解锁前别人锁不上)③非繁忙等待(抢不到锁就挂起,不占 CPU)。

操作函数
初始化pthread_mutex_init(&mutex, NULL)或静态PTHREAD_MUTEX_INITIALIZER
加锁pthread_mutex_lock(&mutex)(抢不到就阻塞)
尝试加锁pthread_mutex_trylock(&mutex)(占用时返回EBUSY,不阻塞)
解锁pthread_mutex_unlock(&mutex)(一般由加锁者解锁)
销毁pthread_mutex_destroy(&mutex)

锁的 4 种属性:PTHREAD_MUTEX_TIMED_NP(默认普通锁,排队公平)/RECURSIVE_NP(嵌套锁,可重复加)/ERRORCHECK_NP(检错锁,重复加锁返回EDEADLK)/ADAPTIVE_NP(适应锁)。

示例:两个线程按顺序遍历数组

charstr[]="abcdefghijklmnopqrstuvwxyz123456789";pthread_mutex_tmutex;intindex2=0;void*t1_exe(void*arg){while(index2<strlen(str)-1){pthread_mutex_lock(&mutex);// 不加锁会出现元素重复打印printf("The %dth element is %c\n",index2,str[index2]);sleep(1);index2++;pthread_mutex_unlock(&mutex);}}// main: pthread_mutex_init(&mutex, NULL); 创建两个线程都跑 t1_exe

3.2 信号量sem_t

头文件<semaphore.h>;互斥用 1 个,同步用多个。本质是 P/V 原语 + 非负计数器。

函数作用
int sem_init(sem_t *sem, int pshared, unsigned int value);pshared=0线程间共享(放全局/堆),非 0 进程间共享(放共享内存)
int sem_wait(sem_t *sem);P:值为 0 阻塞,否则减 1
int sem_trywait(sem_t *sem);P 非阻塞版
int sem_post(sem_t *sem);V:加 1 并唤醒一个等待者(原子)
int sem_destroy(sem_t *sem);销毁
sem_getvalue取当前值

互斥(一个信号量,初值 1,顺序随机但不同时访问)

sem_init(&sem,0,1);// 线程1 // 线程2sem_wait(&sem);sem_wait(&sem);global++;global*=2;sem_post(&sem);sem_post(&sem);

同步(两个信号量,轮流执行)

sem_init(&sem1,0,1);sem_init(&sem2,0,0);// 线程1: sem_wait(&sem1); global++; sem_post(&sem2);// 线程2: sem_wait(&sem2); global*=2; sem_post(&sem1);// 结果固定:1→2→4→5→10…

3.3 条件变量

  • 与互斥锁不同,它用来等待而不是上锁,通常与互斥锁配合。
  • 一个线程等"条件成立"而挂起,另一个线程使"条件成立"并通知。
  • 好处:避免线程反复加锁/解锁却不干活的忙等。
函数作用
pthread_cond_init(&cond, NULL)/PTHREAD_COND_INITIALIZER初始化
pthread_cond_wait(&cond, &mutex)解锁 mutex → 阻塞等待 → 被唤醒后重新加锁
pthread_cond_timedwait(&cond, &mutex, &abstime)超时返回ETIME
pthread_cond_signal(&cond)唤醒一个等待线程
pthread_cond_destroy(&cond)释放

⚠pthread_cond_wait返回后必须重新判断条件:解锁→等待→加锁这三步不是原子操作,等待期间条件可能已被别的线程改变。

pthread_mutex_tmutex;pthread_cond_tcond;inti=1;void*thread1(void*arg)// i 为 9 的倍数时通知 thread2{for(i=1;i<=MAX;i++){pthread_mutex_lock(&mutex);if(i%9==0)pthread_cond_signal(&cond);elseprintf("In thread1:%d\n",i);pthread_mutex_unlock(&mutex);sleep(1);}}void*thread2(void*flag)// 不是 9 的倍数就等{while(i<MAX){pthread_mutex_lock(&mutex);if(i%9!=0)pthread_cond_wait(&cond,&mutex);if(i%9==0)printf("In thread2:%d\n",i);pthread_mutex_unlock(&mutex);sleep(1);}}

四. 三种机制怎么选

机制着眼点
互斥量共享资源的互斥访问(临界区同时只进一个线程)
信号量互斥(1 个)+ 同步(多个)
条件变量等待某个条件成立;配合互斥量避免忙等

互斥/同步会降低一点效率,但能保证共享资源一致;按需选择即可。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 19:59:58

模板代码调试技巧:从行号错位到严格模式完整指南

模板这玩意儿&#xff0c;写得顺手时行云流水&#xff0c;一旦坏掉&#xff0c;很多人当场破防。模板代码调试技巧&#xff0c;我早些年是被行号错位坑到怀疑人生&#xff0c;后来才慢慢摸出一条能复用的路子。这篇文章想把我在实际项目里总结出来的模板调试方法完整讲一遍&…

作者头像 李华
网站建设 2026/10/20 22:08:15

Dos day7

ForeWordHello, everyone. I again meet you in this article, you neednt wait I upgrade passages. In fact, you could study for yourself. And I think you will do better than me. Now!! we start the new war travel!!At First LABEL、MODE、DISKPART、SYSTEMINFO、GPR…

作者头像 李华
网站建设 2026/10/22 3:43:08

数控铣床与加工中心笔记:铣刀与刀柄系统、G54试切对刀、镜像加工指令、孔加工工艺(钻扩铰镗)与固定循环G73/G83/G81/G76/G87/G84

本文整理数控铣床/加工中心编程课程。与数控车床主要加工回转体不同&#xff0c;数控铣床&#xff08;及带刀库、能自动换刀的加工中心&#xff09;主要加工箱体、型腔、平面轮廓、立体轮廓和复杂曲面&#xff08;如模具、叶片&#xff09;&#xff0c;所用刀具、对刀方式和典型…

作者头像 李华
网站建设 2026/10/20 22:08:15

C++项目自动化构建与部署:GitLab+Arbess完整搭建设计

1. 先说说我把C项目搬上自动化构建的真实动机前两年带个小团队维护几个C服务端程序&#xff0c;发布基本靠人肉链路&#xff1a;开发在自己机器上编好&#xff0c;压缩包传到服务器&#xff0c;手动停服务、替换文件、再启动。版本一多就开始乱&#xff0c;有一次漏拷了配置文件…

作者头像 李华
网站建设 2026/10/20 22:08:15

《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 6 章 存储器和时钟信号 阅读笔记 6

《计算机是如何工作的&#xff1a;人人都能懂的计算机软硬件工作原理》第 6 章 存储器和时钟信号 阅读笔记 6 第 6 章 存储器和时钟信号 在前面的章节中&#xff0c;我们看到了如何组合数字逻辑门以产生有用的组合逻辑电路&#xff0c;其输出是输入的函数。 本章将讨论时序逻辑…

作者头像 李华