【Linux】线程同步(第十七篇)

目录

线程同步:

1.概述

2.线程同步的方式

1.互斥锁:

2.读写锁:

3.进程互斥锁:

4.自旋锁:

5.信号量:

6.条件变量COND:


线程同步:

1.概述

多线程模型中,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。

需要安装:

sudo apt-get install manpages-posix
sudo apt-get install manpages-posix-dev

在man里面可以查询到线程同步的相关函数

2.线程同步的方式

1.互斥锁:

可以使用互斥锁对共享资源进行保护

老版本的互斥锁,占用线程解除占用,内核唤醒全部等待线程,多个线程竞争,获取锁资源(惊群效应)

内核会给等待队列中某个线程发送唤醒标记,内定下一个资源使用的线程 占用线程解除占用后,内核会确认队列,确认队列后发送唤醒标记(就近原则) 注意:某个线程持续占用,重复申请,那么大概率该线程一直占用锁资源

互斥锁涉及的函数

pthread_mutex_t lock //互斥锁类型,
pthread_mutex_init(pthread_mutex_t * lock,pthread_mutexattr_t * mattr); //互斥锁初始化
pthread_mutex_destroy(pthread_mutex_t * lock); //互斥锁销毁
pthread_mutex_lock(pthread_mutex_t * lock);//互斥锁加锁
pthread_mutex_unlock(pthread_mutex_t * lock);//互斥锁解锁

只在全局资源访问时加锁

示例:

两个线程同时执行100次 ++,执行结果不一定是200。因为会有线程并发的问题。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
int ncount = 0;       
pthread_mutex_t mlock;
void* thread_joba(void *lpvoid)
{int i =0;while(i++ <5000){usleep(100);pthread_mutex_lock(&mlock);printf("thread_joba is %x,++ncount = %d\n",(unsigned  int)pthread_self(),ncount++);pthread_mutex_unlock(&mlock);}
}
​
void* thread_jobb(void* lpvoid)
{int i =0;while(i++ <5000){pthread_mutex_lock(&mlock);printf("thread_jobb is %x,++ncount = %d\n",(unsigned  int)pthread_self(),ncount++);pthread_mutex_unlock(&mlock);usleep(100);}
}
​
int main()
{pthread_t tid1,tid2;pthread_mutex_init(&mlock,NULL);pthread_create(&tid1,NULL,thread_joba,NULL);pthread_create(&tid2,NULL,thread_jobb,NULL);pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_mutex_destroy(&mlock);return 0;
}

2.读写锁:

可以提高资源访问率,读写锁中写锁为(独占锁)只允许一个线程获取写锁,对全局资源进行写操作,读锁(共享锁)若干个线程可以获取读锁,对全局资源进行读访问 读写锁使用时,读写互斥(进行读操作时,不允许进行写操作,反之亦然)

读写锁涉及到的函数

pthread_rwlock_t lock //读写锁类型
pthread_rwlock_init(pthread_rwlock_t *lock,NULL) //初始化读写锁
pthread_rwlock_destroy(pthread_rwlock_t *lock) //销毁读写锁
pthread_rwlock_wrlock(pthread_rwlock_t *lock) //申请用写锁(独占锁)
pthread_rwlock_rdlock(pthread_rwlock_t *lock) //申请用读锁(共享锁)
pthread_rwlock_unlock(pthread_rwlock_t* lock) //解锁(独占或共享)

示例: 创建8个线程,共享使用一把读写锁,三个写线程使用写锁操作共享资源,五个读线程读访问共享资源

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
int ncount = 0;         
pthread_rwlock_t rwlock;
void* thread_joba(void *lpvoid)
{int i =0;while(i++ <5000){usleep(100);pthread_rwlock_rdlock(&rwlock);printf("thread_joba is %x,read ncount = %d\n",(unsigned  int)pthread_self(),ncount);pthread_rwlock_unlock(&rwlock);}
}
​
void* thread_jobb(void* lpvoid)
{int i =0;while(i++ <5000){pthread_rwlock_wrlock(&rwlock);printf("thread_jobb is %x, write ncount = %d\n",(unsigned  int)pthread_self(),ncount++);pthread_rwlock_unlock(&rwlock);usleep(100);}
}
int main()
{int i =0;pthread_t tid1,tid2;pthread_rwlock_init(&rwlock,NULL);for(i;i<3;i++){pthread_create(&tid1,NULL,thread_joba,NULL);}for(i=3;i<8;i++){pthread_create(&tid2,NULL,thread_jobb,NULL);}pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_rwlock_destroy(&rwlock);return 0;
}

使用场合:log日志系统

快速定位错误异常,有人要写日志, 有人要读日志(排查异常)

3.进程互斥锁:

互斥锁默认情况下使用为线程互斥锁,可以通过设置互斥锁属性,将lock变为进程互斥锁,适用于多进程保护全局资源

pthread_mutexattr_t attr; //互斥锁属性类型
pthread_mutexattr_init(pthread_mutexattr_t *attr); //初始化互斥锁属性。默认时线程互斥锁
pthread_mutexattr_destroy(pthread_mutexattr_t* attr); //销毁释放互斥锁属性
//通过API将attr中的线程互斥锁属性改为进程互斥锁属性
pthread_mutexattr_setpshared(pthread_mutexattr_t* attr,int* pshared);//修改互斥锁属性中的互斥锁类型
pthread_mutexattr_getpshared(pthread mutexattr_t* attr,int* pshared);//获取互斥锁属性中的锁类型
​
pshared 锁类型:PTHREAD_PROCESS_SHARED //进程互斥锁PTHREAD_PROCESS_PRIVATE //线程互斥锁
​
pthread_mutex_init(&lock,&attr); //互斥锁属性准备完成后,在互斥锁初始化时使用

示例: 父子进程利用进程锁,保护全局资源(实现资源访问互斥)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
​
#define onepage 4096
​
typedef struct myshared
{pthread_mutex_t lock;int counter;
}myshared;
int main()
{   pid_t pid;//创建一个用于共享内存映射得文件int fd = open("mmapfile",O_RDWR|O_CREAT,0664);if(fd == -1){printf("open failed \n");exit(0);}//将文件设置为myshared大小ftruncate(fd,sizeof(myshared));myshared *pshared  = (myshared *)mmap(NULL,sizeof(myshared),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//设置互斥锁属性,变为进程互斥锁pthread_mutexattr_t lockattr;pthread_mutexattr_init(&lockattr);pthread_mutexattr_setpshared(&lockattr,PTHREAD_PROCESS_SHARED);pthread_mutex_init(&(pshared->lock),&lockattr);pid = fork();int i =0;if(pid >0){for(i=0;i<50;i++){pthread_mutex_lock(&pshared->lock);printf("parent process counter= %d\n",++pshared->counter);pthread_mutex_unlock(&pshared->lock);}}else if(pid ==0){for(i =0;i<50;i++){      pthread_mutex_lock(&pshared->lock);printf("child  process counter= %d\n",++pshared->counter);pthread_mutex_unlock(&pshared->lock);}}else{printf("fork failed\n");exit(0);}wait(NULL);pthread_mutex_destroy(&pshared->lock);pthread_mutexattr_destroy(&lockattr);munmap(pshared,sizeof(myshared));return 0;
}
​

4.自旋锁:

积极锁(不会阻塞),当某线程占用锁资源时,其他线程会不停自旋尝试获取锁资源,直到占用线程释放为止 优势:自旋锁可以提高资源的利用率,相比互斥锁可以更快的获取资源(与互斥锁不同没有一系列挂起唤醒操作,减少开销) 弊端:使用自旋锁的线程会长时间处于R状态,进线程开销较大

DEFINE_SPINLOCK(spinlock_t lock) 定义并初始化化一个自旋锁int spin_lock_init(spinlock_t *lock) 初始化自旋锁void spin_lock(spinlock_t *lock) 获取指定的自旋锁 即加锁void spin_unlock(spinlock_t *lock) 释放自旋锁 或 解锁int spin_trylock(spinlock_t *lock) 尝试获取指定的自旋锁,如果没有获取到就返回 0int spin_is_locked(spinlock_t *lock) 检查指定的自旋锁是否被获取,如果没有被获取就返回非 0,否则返回 0void spin_lock_irq(spinlock_t *lock) 禁止本地中断,并获取自旋锁void spin_unlock_irq(spinlock_t *lock) 激活本地中断,并释放自旋锁void spin_lock_irqsave(spinlock_t *lock,unsigned long flags) 保存中断状态,禁止本地中断,并获取自旋锁void spin_unlock_irqrestore(spinlock_t*lock, unsigned long flags)将中断状态恢复到以前的状态,并且激活本地中断,释放自旋锁 自旋锁简单使用 1 DEFINE_SPINLOCK(lock) 定义并初始化一个自旋锁/* 线程 A */void functionA (){unsigned long flags;  /* 中断状态 */spin_lock_irqsave(&lock, flags) /* 获取锁 *//* 临界区 */spin_unlock_irqrestore(&lock, flags) /* 释放锁 */}
​/* 中断服务函数 */void irq() {spin_lock(&lock)  /* 获取锁 *//* 临界区 */spin_unlock(&lock) /* 释放锁 */}

5.信号量:

6.条件变量COND:

常见的一些线程并发模型中,多线程无关联,各自执行完成任务,条件变量可以通过指定条件,逻辑让多线程有一个交集场景,让开发者可以通过条件判断,控制工作线程

条件变量可以控制线程的挂起或唤醒(开发者利用条件制定规则,线程严格按照条件规则或执行任务,或等待)

pthread_cond_t  cd//条件变量类型
​
pthread_cond_init(pthread_cond_t *cd,NULL);//初始化条件变量
pthread_cond_destroy(pthread_cond_t *cd ) ; 
pthread_cond_wait(pthread_cond_t* cd, pthread_mutex_t *lock); //通过此函数,可以将线程挂起在特定的条件变量上 
pthread_cond_signal(pthread_cond_t *cd); //唤醒一个挂起在指定条件变量中的线程
​
pthread_cond_broadcast(pthread_cond_t* cd) //唤醒所有挂起在指定条件变量中的线程

示例:两个线程,对全局资源counter进行++操作,,但是区分工作,分奇数偶数

#include  <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
​
pthread_mutex_t mlock;
pthread_cond_t  ca;
pthread_cond_t  cb;
int ncounter = 0;
void* thread_job1(void* lpvoid)
{int i= 0;for(i;i <100;i++){pthread_mutex_lock(&mlock);if(ncounter %2 ==0){pthread_cond_wait(&ca,&mlock);}printf("thread job1 %x ncounter = %d\n",(unsigned int)pthread_self(),++ncounter);pthread_cond_signal(&cb);pthread_mutex_unlock(&mlock);}
}
​
void* thread_job2(void* lpvoid)
{int i= 0;for(i;i <100;i++){pthread_mutex_lock(&mlock);if(ncounter %2 !=0){pthread_cond_wait(&cb,&mlock);}printf("thread job2 %x ncounter = %d\n",(unsigned int)pthread_self(),++ncounter);pthread_cond_signal(&ca);pthread_mutex_unlock(&mlock);}
}
int main()
{ ​pthread_t tid1,tid2;pthread_mutex_init(&mlock,NULL);pthread_cond_init(&ca,NULL);pthread_cond_init(&cb,NULL);pthread_create(&tid1,0,thread_job1,0);pthread_create(&tid2,0,thread_job2,0);pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_mutex_destroy(&mlock);pthread_cond_destroy(&ca);pthread_cond_destroy(&cb);return 0;
}

pthread_cond_wait函数,第一次调用(挂起调用),挂起当前线程并解锁互斥锁 第二次调用(唤醒调用),上锁互斥锁

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

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

相关文章

BLE 协议之链路层

目录 一、前言二、状态和角色三、Air Interface Packets1、Preamble 字段2、Access Address 字段2.1 静态地址2.2 私有地址 3、PDU 字段3.1 Advertising Channel PDU3.1.1 Header 字段3.1.2 Payload 字段 3.2 Data Channel PDU3.2.1 Header 字段3.2.2 Payload 字段 4、CRC 字段…

STM32上实现FFT算法精准测量正弦波信号的幅值、频率和相位差(标准库)

在研究声音、电力或任何形式的波形时&#xff0c;我们常常需要穿过表面看本质。FFT&#xff08;快速傅里叶变换&#xff09;就是这样一种强大的工具&#xff0c;它能够揭示隐藏在复杂信号背后的频率成分。本文将带你走进FFT的世界&#xff0c;了解它是如何将时域信号转化为频域…

【微信小程序】搭建项目步骤 + 引入Tdesign UI

目录 创建1个空文件夹&#xff0c;选择下图基础模板 开启/支持sass 创建公共style文件并引入 引入Tdesign UI: 1. 初始化&#xff1a; 2. 安装后&#xff0c;开发工具进行构建&#xff1a; 3. 修改 app.json 4. 使用 5. 自定义主题色 创建1个空文件夹&#xff0c;选择下…

初始c++:入门基础(完结)

打字不易&#xff0c;留个赞再走吧~~~ 目录 一函数重载二引用1 引⽤的概念和定义2引⽤的特性3引⽤的使⽤三inline四nullptr 一函数重载 C⽀持在同⼀作⽤域中出现同名函数&#xff0c;但是要求这些同名函数的形参不同&#xff0c;可以是参数个数不同或者 类型不同。这样C函数调⽤…

FPGA随记-二进制转格雷码

反射二进制码&#xff08;RBC&#xff09;&#xff0c;也称为反射二进制&#xff08;RB&#xff09;或格雷码&#xff08;Gray code&#xff09;&#xff0c;得名于Frank Gray&#xff0c;是二进制数制的一种排列方式&#xff0c;使得连续两个值之间仅有一个比特&#xff08;二…

【Python进阶】requests库有哪些常用的参数和方法?一篇文章带你详细了解!!!附带源码

常用的requests库参数和方法 常用方法 requests库中定义了多个常用的请求方法&#xff0c;其中requests.get()和requests.post()是最常用的方法。这些方法对应于HTTP协议中的GET和POST方法。 requests.get(url, paramsNone, **kwargs): 用于发送GET请求。requests.post(url…

【高阶数据结构】二叉搜索树的插入、删除和查找(精美图解+完整代码)

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《高阶数据结构》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多《高阶数据结构》点击专栏链接查看&a…

Mysql梳理6——order by排序

目录 6 order by排序 6.1 排序数据 6.2 单列排序 6.3 多行排列 6 order by排序 6.1 排序数据 使用ORDER BY字句排序 ASC&#xff08;ascend&#xff09;:升序DESC(descend):降序 ORDER BY子句在SELECT语句的结尾 6.2 单列排序 如果没有使用排序操作&#xff0c;默认…

一、桥式整流电路

桥式整流电路 1、二极管的单向导电性: 伏安特性曲线: 理想开关模型和恒压降模型 2、桥式整流电流流向过程 输入输出波形: 3、计算:Vo,lo,二极管反向电压。 学习心得

十三,Spring Boot 中注入 Servlet,Filter,Listener

十三&#xff0c;Spring Boot 中注入 Servlet&#xff0c;Filter&#xff0c;Listener 文章目录 十三&#xff0c;Spring Boot 中注入 Servlet&#xff0c;Filter&#xff0c;Listener1. 基本介绍2. 第一种方式&#xff1a;使用注解方式注入&#xff1a;Servlet&#xff0c;Fil…

【C++】——多态详解

目录 1、什么是多态&#xff1f; 2、多态的定义及实现 2.1多态的构成条件 ​2.2多态语法细节处理 2.3协变 2.4析构函数的重写 2.5C11 override 和 final关键字 2.6重载—重写—隐藏的对比分析 3、纯虚函数和抽象类 4、多态的原理分析 4.1多态是如何实现的 4.2虚函数…

OpenCV 2

目录 图像平滑处理 高斯与中值滤波 图像阈值 ​编辑 Canny边缘检测 非极大值抑制 边缘检测效果 轮廓检测方法 ​编辑 ​编辑​编辑 轮廓检测结果 轮廓特征与近似 图像平滑处理 以上两种出来的图片效果 以上的效果&#xff0c;因为填的是normalize False&#xff0c;越界…

零基础到项目实战:Node.js版Selenium WebDriver教程

在当今数字化时代&#xff0c;Web应用程序的质量和性能至关重要。为了确保这些应用的可靠性&#xff0c;自动化测试成为一种不可或缺的工具。Selenium&#xff0c;作为自动化测试领域的瑰宝&#xff0c;为我们提供了无限可能。本教程将深入介绍Selenium&#xff0c;以及如何结合…

硬盘数据恢复必备:4 款强大硬盘数据恢复软件推荐!

在数字化的时代&#xff0c;我们的生活和工作越来越离不开电脑&#xff0c;而硬盘作为重要的数据存储设备&#xff0c;一旦出现数据丢失的情况&#xff0c;往往会给我们带来极大的困扰。别担心&#xff0c;今天就为大家推荐四款强大的硬盘数据恢复软件&#xff0c;帮助你轻松找…

优化算法(三)—模拟退火算法(附MATLAB程序)

模拟退火算法&#xff08;Simulated Annealing, SA&#xff09;是一种基于概率的优化算法&#xff0c;旨在寻找全局最优解。该算法模拟金属退火过程中的物质冷却过程&#xff0c;逐渐降低系统的“温度”以达到全局优化的效果。它特别适用于解决复杂的组合优化问题。 一、模拟退…

[羊城杯 2020]Blackcat1

知识点&#xff1a;数组加密绕过 进入页面熟悉的web三部曲&#xff08;url地址&#xff0c;web源代码&#xff0c;web目录扫描&#xff09; url地址没有什么东西去看看源代码. 这有一个mp3文件点一下看看. 在最后面发现了 PHP源码. if(empty($_POST[Black-Cat-Sheriff]) || em…

Android Studio报错: Could not find pub.devrel:easypermissions:0.3.0, 改用linux编译

在Android studio中去编译开源的仓库&#xff0c;大概率就是各种编译不过&#xff0c;一堆错误&#xff0c;一顿改错&#xff0c;基本上会耗费非常多时间&#xff0c;比如&#xff1a; 这个就是改gradle版本&#xff0c;改成7.2 &#xff0c;修改完成之后&#xff0c;还有其他报…

翻页时钟 2.0-自动置顶显示,点击小时切换显示标题栏不显示标题栏-供大家学习研究参考

更新内容 自动置顶显示点击小时切换显示标题栏&#xff0c;&#xff08;显示标题栏后可移动时钟位置&#xff0c;鼠标拖动边框调整时钟大小&#xff09;不显示标题栏时&#xff0c;透明部分光标可穿透修正一个显示bu 下载地址&#xff1a; https://download.csdn.net/download…

iPhone 16系列:熟悉的味道,全新的体验

来看看iPhone 16和Plus这两个新成员&#xff0c;实话说&#xff0c;它们和之前曝光的样子几乎完全一致。下面我们就一起来细数一下这次的几大变化吧。 外观设计&#xff1a;焕然一新 首先&#xff0c;最显眼的变化就是后置镜头模组的布局调整为了垂直排列。这一改变使得整个背…

小程序开发设计-第一个小程序:安装开发者工具③

上篇文章导航&#xff1a; 小程序开发设计-第一个小程序&#xff1a;注册小程序开发账号②-CSDN博客https://blog.csdn.net/qq_60872637/article/details/142219035?spm1001.2014.3001.5501 须知&#xff1a;不同版本选项有所不同&#xff0c;并无大碍。 第一个小程序&#…