Linux软件编程(6)线程间通信

前两天学习了进程和线程的相关内容,今天来学习线程间的通信。

多线程程序是指多个线程跑在同一个进程里面,所有线程共享进程的全局变量、堆、文件描述符。按理来说,线程之间直接读写全局变量就能交换数据,看着很方便,但这里藏着很大的问题。

一、为什么不能随便直接用全局变量通信

多个线程同时读写同一个全局变量,就会产生资源竞争。

举个简单例子,一个变量count,线程A做count++,线程B也做count++。

count++并不是CPU一条指令直接完成,它分为:读变量、自增、写回内存三步。

如果两个线程刚好在中间互相打断,最后算出来的结果就会出错,这就是竞态问题。

原子操作:不能被CPU任务调度打断的最小操作,一步执行完,中途不会切走别的线程。我们的普通i++不是原子操作。

临界区:加锁和解锁中间包裹的那一段代码。这一块代码,同一时间只允许一个线程进去执行,别的线程要排队等待。

想要安全访问共享资源,就需要互斥锁。

二、互斥锁 pthread_mutex

互斥锁,作用就是保护临界区。谁拿到锁,谁才能执行临界区代码;没拿到锁的线程阻塞等待。

四个核心函数

  1. pthread_mutex_init

    初始化互斥锁。

    参数:锁的地址,第二个attr填NULL使用默认属性。

    返回0代表成功,失败返回非0。

  2. pthread_mutex_destroy

    销毁互斥锁。程序结束前要销毁,释放锁占用的资源。

  3. pthread_mutex_lock

    上锁。如果锁已经被别人拿走,当前线程就阻塞在这里,一直等到锁被释放。

  4. pthread_mutex_unlock

    解锁。用完共享资源一定要解锁,不然别的线程会一直卡死等待。

使用简单流程

pthread_mutex_init 初始化锁

线程内:

pthread_mutex_lock();

// 临界区,操作共享全局变量

pthread_mutex_unlock();

pthread_mutex_destroy(); // 最后销毁

注意:lock和unlock必须成对。中途return、break跳过unlock,就会死锁。

三、死锁

死锁:多个线程互相拿着对方需要的锁,谁都不肯释放,所有线程全部卡住不动,程序彻底僵住。

死锁四个必要条件

  1. 互斥条件:资源同一时刻只能一个线程使用

  2. 不可剥夺条件:已经拿到的锁,不能被别的线程强行抢走

  3. 请求保持:线程已经拿着锁,又去申请别的锁,原有锁不释放

  4. 循环等待:线程A拿锁1等锁2;线程B拿锁2等锁1,形成循环

四个条件全部满足,就会发生死锁。破坏任意一个,就可以避免死锁。

如何避免死锁?

  1. 使用pthread_mutex_trylock,尝试拿锁,拿不到不会阻塞,可以直接返回,避免卡死。

  2. 多把锁的时候,所有线程加锁顺序保持一致。大家都先拿锁A,再拿锁B,就不会出现循环等待。

四、信号量 sem

信号量可以把资源看成一个计数器,不仅仅能做互斥,还可以做线程同步。

资源是有限的,信号量就用来记录可用资源数量,支持初始化、销毁、申请、释放。

四个核心函数

  1. sem_init:初始化信号量
    int sem_init(sem_t *sem, int pshared, unsigned int value);
    • sem:信号量变量地址

• pshared:0代表同一个进程内部线程共享;非0代表进程之间共享

• value:信号量初始值

  1. sem_destroy:销毁信号量,释放资源

  2. sem_wait:申请资源(P操作)

    资源计数大于0,计数减一,直接往下执行;

    资源等于0,线程阻塞等待,直到有资源释放出来。

  3. sem_post:释放资源(V操作)

    信号量计数+1,唤醒正在等待这个信号量的线程。

    小区别:

    互斥锁:同一时间只能一个线程进入临界区。

    信号量:初始值可以设置大于1,可以允许多个线程同时获取资源。

    五、总结

  4. 互斥锁(mutex):主要用于互斥访问,保护临界区,锁只有两种状态:上锁、解锁。

  5. 信号量(sem):既可以实现互斥,更擅长做同步,控制线程执行先后顺序,自带计数器。

    互斥锁、信号量,只解决同一个进程内部线程之间通信。

    进程之间通信,要用管道、FIFO、消息队列这些,进程之间虚拟地址互相隔离,全局变量不能直接互通。

相关推荐
考虑考虑8 小时前
JDK26中的List.ofLazy()
java·后端·java ee
小蒜学长8 小时前
基于SpringBoot的公寓报修管理系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·公寓报修管理系统·多角色协同
IT古董8 小时前
《FDE前沿部署工程师实战教程》33 - Enterprise AI Observability:从Agent Trace到全链路智能运维
数据库·人工智能
for_ever_love__9 小时前
MySQL 主从复制讲透:binlog 原理、搭建步骤与读写分离
java·数据库·mysql·binlog·读写分离·原理·主从复制
Ada's9 小时前
【计算机基础系列】003:Python数据结构
开发语言·数据结构·python
2601_962885729 小时前
如何用 Python 计算 TRIX 三重指数平滑均线指标?
开发语言·python
还卿一钵无情泪10 小时前
Unsloth 微调 构建自己的大模型 没有GPU也能微调
linux·开发语言·人工智能·python·大模型·nlp·unsloth
小π军11 小时前
操作系统面试题
java·linux·服务器
倔强的石头10611 小时前
应用账号最小权限实践:读写账号、报表账号、运维账号分层
java·运维·开发语言