linux基础——linux线程间通信及同步机制总结

2023-10-26

线程间的通信有两种情况:

1、一个进程中的线程与另外一个进程中的线程通信,由于两个线程只能访问自己所属进程的地址空间和资源,故等同于进程间的通信。

2、同一个进程中的两个线程进行通信。本文说的就是第二种情况。

关于进程间通信(IPC)可以看我的另一篇博文

比起进程复杂的通信机制(管道、匿名管道、消息队列、信号量、共享内存、内存映射以及socket等),线程间通信要简单的多。

因为同一进程的不同线程共享同一份全局内存区域,其中包括初始化数据段、未初始化数据段,以及堆内存段,所以线程之间可以方便、快速地共享信息。只需要将数据复制到共享(全局或堆)变量中即可。不过,要避免出现多个线程试图同时修改同一份信息。

下图为多线程的进程地址空间:



线程安全:

所在的进程中有多个线程在同时运行,而这些线程可能会同时某一段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。线程安全就是说多线程访问同一段代码不会产生不确定的结果。编写线程安全的代码依靠 线程同步

线程间的同步:

如果变量时只读的,多个线程同时读取该变量不会有一致性问题,但是,当一个线程可以修改的变量,其他线程也可以读取或者修改的时候,我们就需要对这些线程进行同步,确保它们在访问变量的存储内容时不会访问到无效的值。

1. 互斥锁

互斥量本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量。对互斥量进行枷锁以后,其他视图再次对互斥量加锁的线程都会被阻塞直到当前线程释放该互斥锁。如果释放互斥量时有一个以上的线程阻塞,那么所有该锁上的阻塞线程都会变成可运行状态,第一个变成运行状态的线程可以对互斥量加锁,其他线程就会看到互斥量依然是锁着,只能再次阻塞等待它重新变成可用,这样,一次只有一个线程可以向前执行。
常用头文件:
#include <pthread.h>
常用函数:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);//互斥初始化
int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁互斥
int pthread_mutex_lock(pthread_mutex_t *mutex);//锁定互斥
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁互斥
int pthread_mutex_trylock(pthread_mutex_t *mutex);//销毁互斥
eg.pthread_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
...
pthread_mutex_unlock(&mutex);
pthread_mutex_detroy(&mutex);

互斥量的死锁:
一个线程需要访问两个或者更多不同的共享资源,而每个资源又有不同的互斥量管理。当超过一个线程加锁同一组互斥量时,就可能发生死锁。 死锁就是指多个线程/进程因竞争资源而造成的一种僵局(相互等待),若无外力作用,这些进程都将无法向前推进。
死锁的处理策略:
1、预防死锁:破坏死锁产生的四个条件:互斥条件、不剥夺条件、请求和保持条件以及循环等待条件。
2、避免死锁:在每次进行资源分配前,应该计算此次分配资源的安全性,如果此次资源分配不会导致系统进入不安全状态,那么将资源分配给进程,否则等待。算法:银行家算法。
3、检测死锁:检测到死锁后通过资源剥夺、撤销进程、进程回退等方法解除死锁。

2. 读写锁

读写锁与互斥量类似,不过读写锁拥有更高的并行性。互斥量要么是锁住状态,要么是不加锁状态,而且一次只有一个线程可以对其加锁。读写锁有3种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。
当读写锁是写加锁状态时,在这个锁被解锁之前,所有视图对这个锁加锁的线程都会被阻塞。当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是任何希望以写模式对此锁进行加锁的线程都会阻塞,直到所有的线程释放它们的读锁为止。
常用头文件:
#include <pthread.h>
常用函数:
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *rwlockattr);//初始化读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);//销毁读写锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//读模式锁定读写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//写模式锁定读写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解锁读写锁
eg.pthread_rwlock_t q_lock;
pthread_rwlock_init(&q_lock, NULL);
pthread_rwlock_rdlock(&q_lock);
...
pthread_rwlock_unlock(&q_lock);
pthread_rwlock_detroy(&q_lock);

3. 条件变量

条件变量是线程可用的另一种同步机制。互斥量用于上锁,条件变量则用于等待,并且条件变量总是需要与互斥量一起使用,运行线程以无竞争的方式等待特定的条件发生。
条件变量本身是由互斥量保护的,线程在改变条件变量之前必须首先锁住互斥量。其他线程在获得互斥量之前不会察觉到这种变化,因为互斥量必须在锁定之后才能计算条件。
常用头文件:
#include <pthread.h>
常用函数:
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);//初始化条件变量
int pthread_cond_destroy(pthread_cond_t *cond);//销毁条件变量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);//无条件等待条件变量变为真
int pthread_cond_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *tsptr);//在给定时间内,等待条件变量变为真
eg.pthread_mutex_t mutex;
pthread_cond_t cond;
...
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
...
pthread_mutex_unlock(&mutex);
...
注意:    pthread_cond_wait 执行的流程首先将这个mutex解锁, 然后等待条件变量被唤醒, 如果没有被唤醒, 该线程将一直休眠, 也就是说, 该线程将一直阻塞在这个pthread_cond_wait调用中, 而当此线程被唤醒时, 将自动将这个mutex加锁,然后再进行条件变量判断(原因是“惊群效应”,如果是多个线程都在等待这个条件,而同时只能有一个线程进行处理,此时就必须要再次条件判断,以使只有一个线程进入临界区处理。),如果满足,则线程继续执行。

4. 信号量

线程的信号和进程的信号量类似,使用线程的信号量可以高效地完成基于线程的资源计数。信号量实际上是一个非负的整数计数器,用来实现对公共资源的控制。在公共资源增加的时候,信号量就增加;公共资源减少的时候,信号量就减少;只有当信号量的值大于0的时候,才能访问信号量所代表的公共资源。
常用头文件:
#include <semaphore.h>
常用函数:
sem_t sem_event;
int sem_init(sem_t *sem, int pshared, unsigned int value);//初始化一个信号量 
int sem_destroy(sem_t * sem);//销毁信号量
int sem_post(sem_t * sem);//信号量增加1
int sem_wait(sem_t * sem);//信号量减少1
int sem_getvalue(sem_t * sem, int * sval);//获取当前信号量的值

互斥与同步的区别:
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:主要是流程上的概念,是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
互斥锁、条件变量和信号量的区别:
互斥锁:互斥,一个线程占用了某个资源,那么其它的线程就无法访问,直到这个线程解锁,其它线程才可以访问。
条件变量:同步,一个线程完成了某一个动作就通过条件变量发送信号告诉别的线程,别的线程再进行某些动作。条件变量必须和互斥锁配合使用。
信号量:同步,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作。而且信号量有一个更加强大的功能,信号量可以用作为资源计数器,把信号量的值初始化为某个资源当前可用的数量,使用一个之后递减,归还一个之后递增。
另外还有以下几点需要注意:
1、信号量可以模拟条件变量,因为条件变量和互斥量配合使用,相当于信号量模拟条件变量和互斥量的组合。在生产者消费者线程池中,生产者生产数据后就会发送一个信号 pthread_cond_signal通知消费者线程,消费者线程通过pthread_cond_wait等待到了信号就可以继续执行。这是用条件变量和互斥锁实现生产者消费者线程的同步,用信号量一样可以实现!
2、信号量可以模拟互斥量,因为互斥量只能为加锁或解锁(0 or 1),信号量值可以为非负整数,也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量时,就完成一个资源的互斥访问。前面说了,信号量主要用做多线程多任务之间的同步,而同步能够控制线程访问的流程,当信号量为单值时,必须有线程释放,其他线程才能获得,同一个时刻只有一个线程在运行(注意,这个运行不一定是访问资源,可能是计算)。如果线程是在访问资源,就相当于实现了对这个资源的互斥访问。
3、互斥锁是为上锁而优化的;条件变量是为等待而优化的; 信号量既可用于上锁,也可用于等待,因此会有更多的开销和更高的复杂性。
4、互斥锁,条件变量都只用于同一个进程的各线程间,而信号量(有名信号量)可用于不同进程间的同步。当信号量用于进程间同步时,要求信号量建立在共享内存区。
5、互斥量必须由同一线程获取以及释放,信号量和条件变量则可以由一个线程释放,另一个线程得到。
6、信号量的递增和减少会被系统自动记住,系统内部的计数器实现信号量,不必担心丢失,而唤醒一个条件变量时,如果没有相应的线程在等待该条件变量,此次唤醒会被丢失。

5. 自旋锁

自旋锁与互斥量类似,但它不是通过休眠使进程阻塞,而是在获取锁之前一直处于忙等(自旋)阻塞状态。自旋锁可以用于以下情况:锁被持有的时间短,而且线程并不希望在重新调度上花费太多的成本。

6. 屏障
屏障是指用户可以协调多个线程并行工作的同步机制。屏障允许每个线程等待,直到所有的合作线程都到达某一点,然后从改点继续执行。
还记pthread_join函数吗?在子线程退出之前,主线程要一直等待。pthread_join函数就是一种屏障,它允许一个线程等待,直到另一个线程退出。屏障允许任意数量的线程等待,直到所有的线程完成处理工作,而线程不需要退出。所有线程达到屏障后可以接着工作。
如果我们要让主线程在所有工作线程完成之后再做某项任务,一般把屏障计数值设为工作线程数加1,主线程也作为其中一个候选线程。

参考:
《unix环境高级编程》


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux基础——linux线程间通信及同步机制总结 的相关文章

  • Linux shell 根据第二列对文件进行排序?

    我有一个这样的文件 FirstName FamilyName Address PhoneNumber 如何按 FamilyName 排序 如果这是 UNIX sort k 2 file txt 您可以使用多个 k用于对多列进行排序的标志 例
  • linux新手关于嵌入式linux设备驱动的问题

    最近在研究linux驱动 正如我读过的那些文章所说 设备驱动程序模块很可能会根据内核的需要自动加载 因此我想知道内核如何确定为特定设备 声卡 I2C spi 设备 等 我也无法彻底想象内核如何在启动时检测每个硬件设备 与嵌入式linux相关
  • 如果文件没有行尾字符,则 wc -l 不计算文件的最后一个

    我需要计算 unix 文件的所有行数 该文件有 3 行 但是wc l仅给出 2 个计数 我知道它不计算最后一行 因为它没有行尾字符 任何人都可以告诉我如何计算这一行吗 grep c返回匹配行的数量 只需使用一个空字符串 作为您的匹配表达式
  • 何时用引号将 shell 变量括起来?

    我应该或不应该在 shell 脚本中用引号括住变量吗 例如 下列说法正确的是 xdg open URL eq 2 or xdg open URL eq 2 如果是这样 为什么 一般规则 如果它可以为空或包含空格 或实际上任何空格 或特殊字符
  • 我想在 Red Hat Linux 服务器中执行 .ps1 powershell 脚本

    我有一个在窗口中执行的 ps1 powershell 脚本 但我的整个数据都在 Linux 服务器中 有什么可能的方法可以让我在红帽服务器中执行 powershell 脚本 powershell脚本是 Clear Host path D D
  • 使用 ioctl 在 C++ 中以编程方式添加路由

    我编写了简单的 C 函数 添加了新路线 void addRoute int fd socket PF INET SOCK DGRAM IPPROTO IP struct rtentry route memset route 0 sizeof
  • MySQL 与 PHP 的连接无法正常工作

    这是我的情况 我正在尝试使用 Apache 服务器上的 PHP 文件连接到 MySQL 数据库 现在 当我从终端运行 PHP 时 我的 PHP 可以连接到 MySQL 数据库 使用 php f file php 但是当我从网页执行它时 它只
  • Docker忽略limits.conf(试图解决“打开文件太多”错误)

    我正在运行一个 Web 服务器 该服务器正在处理数千个并发 Web 套接字连接 为了实现这一点 在 Debian linux 我的基本镜像是 google debian wheezy 在 GCE 上运行 上 打开文件的默认数量设置为 100
  • 为什么此 NASM 代码会打印我的环境变量?

    本学期我刚刚完成计算机体系结构课程 除其他外 我们一直在涉足 MIPS 汇编并在 MARS 模拟器中运行它 今天 出于好奇 我开始在我的 Ubuntu 机器上摆弄 NASM 基本上只是将教程中的内容拼凑起来 并感受一下 NASM 与 MIP
  • Linux shell 从用户输入中获取设备 ID

    我正在为一个程序编写安装脚本 该程序需要在其配置中使用 lsusb 的设备 ID 因此我正在考虑执行以下操作 usblist lsusb put the list into a array for each line use the arr
  • Crontab 每 5 分钟一次 [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我如何告诉 crontab 每 5 分钟运行一次 但从每小时的第二分钟开始 换句话说 我想在以下时间执行我的脚本minute 5 2 例如 我的脚本应
  • LINUX:如何锁定内存中进程的页面

    我有一个 LINUX 服务器 运行一个具有大量内存占用的进程 某种数据库引擎 该进程分配的内存太大 需要将其中一部分换出 换出 我想做的是将所有其他进程 或正在运行的进程的子集 的内存页面锁定在内存中 以便只有数据库进程的页面被换出 例如
  • 来自守护程序的错误响应:加入会话密钥环:创建会话密钥:超出磁盘配额

    我尝试在我的服务器上安装 docker 使用本教程 https docs docker com install linux docker ce ubuntu 我想远程运行 docker 镜像并使用 portainer Web 界面来管理一切
  • 如何在不使用 IDE 的情况下在 Linux 上运行 Java 项目

    我是 Java 新手 基本上 我开发了一个java项目 其中包含Eclipse中的多个Java包 该项目在我安装了 redhat Linux 的桌面上运行正常 然而 我需要在一个更强大的没有安装X11的Linux服务器 redhat ent
  • ssh 连接超时

    我无法在 git 中 ssh 到 github bitbucket 或 gitlab 我通常会收到以下错误消息 如何避免它 输出 ssh T email protected cdn cgi l email protection i ssh
  • Tomcat Intellij Idea:远程部署

    RackSpace 云服务器 Ubuntu 12 04 Intellij Idea 11 1 2 Windows 8 Tomcat 7 0 26 JDK 6 在 Intellij Idea 上 当我尝试在远程 Tomcat 7 服务器上运行
  • vmsplice() 和 TCP

    在原来的vmsplice 执行 有人建议 http lwn net Articles 181169 如果您的用户态缓冲区是管道中可容纳的最大页面数的 2 倍 则缓冲区后半部分成功的 vmsplice 将保证内核使用缓冲区的前半部分完成 但事
  • 为什么 Linux 原始套接字的 RX 环大小限制为 4GB?

    背景 我试图mmap 我的原始套接字的 RX 环形缓冲区64 bitLinux 应用程序 我的环由 4096 个块组成 每个块大小为 1MB 总共 4GB 请注意 每个 1MB 块中可以有许多帧 如果您好奇 请参阅此文档了解背景信息 htt
  • 仅使用containerd(不使用Docker)修剪容器镜像

    如果我刚刚containerd安装在 Linux 系统上 即 Docker 是not安装 如何删除未使用的容器映像以节省磁盘空间 Docker 就是这么方便docker system prune https docs docker com
  • 有没有一种快速方法可以从 Jar/war 中删除文件,而无需提取 jar 并重新创建它?

    所以我需要从 jar war 文件中删除一个文件 我希望有类似 jar d myjar jar file I donot need txt 的内容 但现在我能看到从 Linux 命令行执行此操作的唯一方法 不使用 WinRAR Winzip

随机推荐

  • Fckeditor常见漏洞的挖掘与利用整理汇总

    查看编辑器版本 FCKeditor whatsnew html 2 Version 2 2 版本 Apache linux 环境下在上传文件后面加个 突破 测试通过 3 Version lt 2 4 2 For php 在处理PHP 上传的
  • Django 快速搭建博客 第十一节(文章阅读量统计,自动生成文章摘要)

    这一节主要做一些修补工作 一个是 文章阅读量的统计 另一个是自动生成文章摘要内容 1 文章阅读量的统计 1 文章阅读量的统计 我们需要在model下的Post类中新加入一个views 字段用来统计文章被阅读的数量 blog models p
  • 是否二叉搜索树

    习题4 3 是否二叉搜索树 25分 本题要求实现函数 判断给定二叉树是否二叉搜索树 函数接口定义 bool IsBST BinTree T 其中BinTree结构定义如下 typedef struct TNode Position type
  • Go语言函数

    http www jb51 net article 56831 htm Go语言中的函数有系统函数和自定义函数 1 系统函数 系统函数就是Go语言自带的函数 系统函数一般根据功能封装在不同的包内 比如Print Printf Println
  • 微信聊天记录导出工具WeChatExporter开源啦!

    2019年08月21日更新 距离第一次发布软件已经有了许多新功能和稳定性上的提升 本文的一些内容已经过时 欢迎直接到GitHub上看ReadMe https github com tsycnh WeChatExporter 之前曾经写过一个
  • 消息队列 - RabbitMQ - 拓展

    1 Message 状态 Message 在投递时 如果当前 Queue 没有 Message 且有 Consumer 已经订阅了这个 Queue 那么该 Message 会直接发送给 Consumer 不会经过 Queue 存储 Mess
  • 在 Substance Painter中自定义Shader

    为什么要学习在Substance Painter中自定义Shader 答 需要实现引擎与Substance Painter中的渲染效果一致 材质的配置也一致 所见即所得 基础概述 首先在着色器设置这里 我们可以查看当前渲染使用的着色器 如果
  • ETL笔记——第五章 数据清洗与校验(数据检验)

    一 数据一致性处理 通过Kettle工具 使用弱一致性对数据表Personnel Information中的数据进行一致性处理 即利用数据表Personnel Information中的字段GENDER中的值训练出一个健康值预测模型 用于将
  • Android学习之Activity源码的理解(一)

    一 Activity为Android系统中四大组件之一 是Android程序的呈现层 并通过界面与用户进行交互 因此理解Activity源码是有必要的 二 之前我写过一篇文章 http blog csdn net u012561176 ar
  • scrapy的工作流程

    scrapy的工作流程如下图所示 整个工作流程 爬虫中起始的url构造成request对象 并传递给调度器 引擎从调度器中获取到request对象 然后交给下载器 由下载器来获取到页面源代码 并封装成response对象 并回馈给引擎 引擎
  • 测试开发-面试题目整理

    1 java的三大特性 封装 继承 多态 2 python的三大特性 封装 继承 多态 3 多态是怎么实现的 4 重载和重写的区别是什么 5 java的八大数据类型 6 花旗金融算法 java的冒泡法怎么实现的 几层for循环 7 得物面试
  • java网络编程——NIO架构

    目录 1 什么是NIO 2 NIO结构 3 基于NIO下的聊天系统实现 4 Netty 1 什么是NIO NIO java non blocking IO 同步非阻塞IO BIO是阻塞IO 即每一个事件都需要分配一个进程给他 如果客户端没有
  • Debian9 设置静态IP

    1 查看虚拟机上本机ip cmd ipconfig 2 配置网卡 2 1 备份原有配置文件配置文件 cp etc network interfaces etc network interfacesbak 备份原有配置文件 2 2 编辑int
  • elm分类器功能_一文带你读懂线性分类器

    本文为 AI 研习社编译的技术博客 原标题 Linear Classifier 作者 Thomas Pernet 翻译 邓普斯 杰弗 涂世文 Disillusion 校对 邓普斯 杰弗 审核 酱番梨 整理 菠萝妹 原文链接 https me
  • 前端h5 播放器vue-video-player

    1 安装依赖 npm install vue video player 2 在main js全局引入 import VideoPlayer from vue video player import video js dist video j
  • 计蒜客 - 44280 UnDetected(并查集).md

    题目大意 题目链接 给你n个圆 ans 为 最少 前多少个 圆 能把x轴 0 200 完全覆盖 完全覆盖是相交的圆 的最左端 lt 0 最右端 gt 200 输出ans 1 分析 并查集维护边界和输入的圆是否相交 代码 1 2 3 4 5
  • 【大数据】Doris:基于 MPP 架构的高性能实时分析型数据库

    Doris 基于 MPP 架构的高性能实时分析型数据库 1 Doris 介绍 Apache Doris 是一个基于 MPP Massively Parallel Processing 大规模并行处理 架构的高性能 实时的分析型数据库 以极速
  • java上传实现 spring boot +element ui

    先从element ui el upload组件开始介绍
  • linux虚拟机ifconfig command not found

    在linux虚拟机中输入ifconfig命令 出现ifconfig command not found 以下是排查过程 1 cd sbin然后ls 没找到ifconfig命令 2 想通过yum install net tools安装 发现出
  • linux基础——linux线程间通信及同步机制总结

    线程间的通信有两种情况 1 一个进程中的线程与另外一个进程中的线程通信 由于两个线程只能访问自己所属进程的地址空间和资源 故等同于进程间的通信 2 同一个进程中的两个线程进行通信 本文说的就是第二种情况 关于进程间通信 IPC 可以看我的另