Linux线程同步

news2024/11/24 20:42:07

同步的几种方式:信号量,互斥锁,条件变量,读写锁

同步:对程序的执行过程进行控制,保证对临界资源的访问同一时刻只能有一个进程或线程访问。

2.1信号量

存在P操作:获取资源,信号量值-1;和V操作:释放资源,信号量值+1。

临界资源:同一时刻只允许一个线程或进程访问的资源。

临界区:访问临界资源的代码段。

二值信号量:信号量的值只有0和1

计数信号量:信号量值最大值大于1

2.2线程信号量接口函数与应用

sem_init(sem_t* sem , int pshared , unsigned int value) 初始化信号量 第一个参数为信号量地址;第二个参数为是否在两个进程间共享,不共享就传0;第三个为初始值

sem_wait(sem_t* sem) P操作

sem_post(sem_t* sem) V操作

sem_destroy(sem_t* sem) 销毁信号量

场景实现:三个线程分别能打印A B C,使用信号量实现顺序打印ABCABCABC...

实现原理:

实现代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<semaphore.h>
#include<pthread.h>

sem_t sema,semb,semc;
void* funa(void* arg)
{
    for(int i=0;i<5;++i)
    {
        sem_wait(&sema);
        printf("A");
        fflush(stdout);
        sem_post(&semb);
    }
}
void* funb(void* arg)
{
    for(int i=0;i<5;++i)
    {
        sem_wait(&semb);
        printf("B");
        fflush(stdout);
        sem_post(&semc);
    }
}
void* func(void* arg)
{
    for(int i=0;i<5;++i)
    {
        sem_wait(&semc);
        printf("C");
        fflush(stdout);
        sem_post(&sema);
    }
}
int main()
{
    sem_init(&sema,0,1);
    sem_init(&semb,0,0);
    sem_init(&semc,0,0);
    
    pthread_t id1,id2,id3;
    pthread_create(&id1,NULL,funa,NULL);
    pthread_create(&id2,NULL,funb,NULL);
    pthread_create(&id3,NULL,func,NULL);
    
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    pthread_join(id3,NULL);
    
    sem_destroy(&sema);
    sem_destroy(&semb);
    sem_destroy(&semc);
    exit(0);
}

运行结果:

 

2.3互斥锁(互斥量)

操作: 加锁 解锁

相关函数:

pthread_mutex_init() 初始化

pthread_mutex_lock() 加锁 会存在阻塞

pthread_mutex_unlock() 解锁

pthread_mutex_destroy() 销毁

场景使用:(以1.6的代码为例,使用互斥锁实现不会出现小于5000)

 

#define MAXID  5
int val=1;//全局变量
pthread_mutex_t mutex;

void* fun(void* arg)
{
    for(int i=0;i < 1000;++i)
    {
        pthread_mutex_lock(&mutex);
        printf("%d\n",val++);
        pthread_mutex_unlock(&mutex);
    }
}
int main()
{
    pthread_mutex_init(&mutex,NULL);
    pthread_t id[MAXID];
    for(int i = 0;i < MAXID; ++i)
    {
        pthread_create(&id[i],NULL,fun,NULL);
    }
    for(i = 0;i < MAXID; ++i)
    {
        pthread_join(id[i],NULL);
    }
    pthread_mutex_destroy(&mutex);
    exit(0);
}

运行结果总是到5000,不会出现小于5000的情况

 

上述操作可以使用信号量代替,能使用互斥锁的情况一定也可以使用信号量。该种情况使用互斥锁方便,而信号量需要设置初值等。

2.4读写锁

接口函数:

pthread_rwlock_init() 初始化

pthread_rwlock_rdlock() 读锁

pthread_rwlock_wrlock() 写锁

pthread_rwlock_unlock() 解锁

pthread_rwlock_destroy() 销毁

与互斥锁的区别:对于多个线程只数据时,读数据的线程因为都不会改变数据,就可以同时执行。使用互斥锁同一时刻只能有一个线程读取,会造成程序性能降低。而读写锁分为读锁和写锁,如果对于多个不同线程都加的是读锁的话,多个线程会同时去读数据。只有多个读可以一起执行,一读一写或者多个写都不可以,会进行阻塞。

适用场景:读取数据的线程多,写的线程少。

模拟使用读写锁:(两个读一个写)

pthread_rwlock_t lock; 
void* fun_r1(void* arg)
{
    for(int i=0;i<10;++i)
    {
        pthread_rwlock_rdlock(&lock);
        printf("fun1 read start\n");
        sleep(1);
        printf("fun1 read end\n");
        pthread_rwlock_unlock(&lock);
    }
}
void* fun_r2(void* arg)
{
    for(int i=0;i<5;++i)
    {
        pthread_rwlock_rdlock(&lock);
        printf("fun2 read start\n");
        sleep(2);
        printf("fun2 read end\n");
        pthread_rwlock_unlock(&lock);
    }
}
void* fun_w(void* arg)
{
    for(int i=0;i<3;++i)
    {
        pthread_rwlock_wrlock(&lock);
        printf("------write start------\n");
        sleep(3);
        printf("------write end------\n");
        pthread_rwlock_unlock(&lock);
    }
}
int main()
{
    pthread_rwlock_init(&lock,NULL);
    pthread_t id1,id2,id3;
    pthread_create(&id1,NULL,fun_r1,NULL);
    pthread_create(&id2,NULL,fun_r2,NULL);
    pthread_create(&id3,NULL,fun_w,NULL);
    pthread_join(&id1,NULL);
    pthread_join(&id2,NULL);
    pthread_join(&id3,NULL);
    pthread_rwlock_destroy(&lock);
    exit(0);
}

 

只要写操作一启动,则两个读操作都无法抢占,而在fun2去执行的同时fun1也可以去执行。即多个读操作可以同时执行,而写操作同时只能一个执行。

2.5条件变量

接口函数:

pthread_cond_init() 初始化

pthread_cond_broadcast() 唤醒在条件变量上等待的所有线程

pthread_cond_signal() 唤醒在条件变量上等待的一个线程

pthread_cond_wait() 添加线程进入条件变量的等待队列

第一个参数为条件变量地址;第二个参数为互斥锁地址,目的是为了解锁和加锁,所以才使用次函数前需要加锁,后需要解锁。因为进入等待队列时为了确保同一时间只能有一个线程入队才需要手动加锁,进入完成后wait函数内部会实现解锁;然后阻塞住;直到被唤醒后,该线程就会出队列,为了确保同一时间只能有一个线程出队,wait函数内部会实现加锁,然后出队后手动解锁。

上述的手动是需要自己加入加锁和解锁函数

pthread_cond_destroy() 销毁

实现三个线程模拟使用条件变量

pthread_mutex_t mutex;
pthread_cond_t cond;
void funa(void* arg)
{
    char* s=(char*)arg;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond,&mutex);//该线程加入等待队列后,会在wait函数内部释放锁,使得其他线程可以继续使用该互斥锁。
        pthread_mutex_unlock(&mutex);
        //因为进入等待队列时为了确保同一时间只能有一个线程入队才需要手动加锁,进入完成后wait函数内部会实现解锁;然后阻塞住,等到该线程出队列时,为了确保同一时间只能有一个线程出队,wait函数内部会实现加锁,然后出队后手动解锁
        if(strncmp(s,"end",3)==0)
        {
            break;
        }
        printf("A thread read :%s",s);
    }
    printf("funa over\n");
}
void funb(void* arg)
{
    char* s=(char*)arg;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond,&mutex);
        pthread_mutex_unlock(&mutex);
        if(strncmp(s,"end",3)==0)
        {
            break;
        }
        printf("B thread read :%s",s);
    }
    printf("funb over\n");
}
int main()
{
    pthread_mutex_init(&mutex,NULL);
    pthread_cond_init(&cond,NULL);
    char buff[128]={0};
    pthread_t ida,idb;
    pthread_create(&ida,NULL,funa,buff);
    pthread_create(&idb,NULL,funb,buff);
    while(1)
    {
        fgets(buff,128,stdin);
        if(strncmp(buff,"end",3)==0)
        {
            pthread_cond_broadcast(&cond);
            break;
        }
        else
        {
            pthread_cond_signal(&cond);
        }
    }
    pthread_join(ida,NULL);
    pthread_join(idb,NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    exit(0);
}

运行结果

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

58.最后一个单词的长度

LeetCode-58.最后一个单词的长度 1、题目描述2、解题思路3、代码实现4、解题记录 1、题目描述 题目描述&#xff1a; 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任…

通讯录获取APP程序分析

前言 APP非法获取用户通讯录等隐私信息 我用技术分析APP是如何获取信息的 如果你不幸中招了&#xff0c;可以参考下方链接(有偿) 我的方法是替换掉通讯录数据&#xff0c;替换不成功包退&#xff01; 每日16:00-06:00在线&#xff0c;5分钟受理&#xff0c;2~3小时完成 点下面…

下载安装mysql与设置密码详细步骤(压缩包版本)

目录 一、前言 二、操作步骤 &#xff08;一&#xff09;下载与解压缩 &#xff08;二&#xff09;配置环境变量 &#xff08;三&#xff09;安装MySQL服务 &#xff08;四&#xff09;设置ini文件和data文件 &#xff08;五&#xff09;启动MySQL服务和设置密码 三、…

【C++ 程序设计】第 5 章:类的继承与派生

目录 一、类的继承与类的派生 &#xff08;1&#xff09;继承的概念 &#xff08;2&#xff09;派生类的定义与大小 ① 派生类的定义 ② 派生类的大小 &#xff08;3&#xff09;继承关系的特殊性 &#xff08;4&#xff09;有继承关系的类之间的访问 &#xff08;5&am…

多线程单例模式

1、单例模式 顾名思义&#xff0c;单例模式能保证某个类在程序中只存在唯一一份示例&#xff0c;而不会创建出多个实例。就像java的JDBC编程只需要创建一个单例类DataSourece从这个DataSorce中获取数据库连接。没必要创建多个对象。 单例模式具体实现方式分为“饿汉”和“懒汉…

java编译与反编译

参考&#xff1a; Idea 使用技巧记录_source code recreated from a .class file by intell_hresh的博客-CSDN博客 深入理解Java Class文件格式&#xff08;一&#xff09;_昨夜星辰_zhangjg的博客-CSDN博客 实践详解javap命令&#xff08;反编译字节码&#xff09;_天然玩家…

【运筹优化】元启发式算法详解:迭代局部搜索算法(Iterated Local Search,ILS)+ 案例讲解代码实现

文章目录 一、介绍二、迭代局部搜索2.1 总体框架2.2 随机重启2.3 在 S* 中搜索2.4 ILS 三、获得高性能3.1 初始解决方案3.2 Perturbation3.2.1 扰动强度3.2.2 自适应扰动3.2.3 更复杂的扰动方案3.2.4 Speed 3.3 接受准则3.4 Local Search3.5 ILS 的全局优化 四、ILS 的精选应用…

Windows PE怎么修复系统?使用轻松备份解决!

​什么是Windows PE? Windows预先安装环境&#xff08;英语&#xff1a;Microsoft Windows Preinstallation Environment&#xff09;&#xff0c;简称Windows PE或WinPE&#xff0c;是Microsoft Windows的轻量版本&#xff0c;主要提供个人电脑开发商&#xff08;主要为OEM厂…

electron+vue3全家桶+vite项目搭建【20】窗口事件广播,通用事件封装

引入 electron中的渲染进程与主进程之间的数据交互需要利用ipc通信&#xff0c;互相订阅/通知来实现&#xff0c;我们不妨封装一个通用事件广播&#xff0c;利用自定义的事件名称来让主进程遍历窗口挨个推送对应内容&#xff0c;来实现事件的广播。 demo项目地址 实现思路 …

【计算机视觉】MaskFormer:将语义分割和实例分割作为同一任务进行训练

文章目录 一、导读二、逐像素分类和掩码分类的区别2.1 逐像素分类2.2 掩码分类2.3 区别 三、DETR四、MaskFormer五、MaskFormer用于语义和实例分割六、总结 一、导读 目标检测和实例分割是计算机视觉的基本任务&#xff0c;在从自动驾驶到医学成像的无数应用中发挥着关键作用。…

模拟电路系列分享-运放的关键参数5

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示&#xff1a;这里可以添加技术概要 例如&#xff1a; 实际运放与理想运放具有很多差别。理想运放就像一个十全十美的人&#xff0c;他学习100 分&#xff0c;寿命无限长&#xff0c;长得没挑剔&#xff0c;而实…

【c++11】移动构造的性质 和 与拷贝构造的比较(详解)

文章目录 定义性质移动构造的定义实例代码分析移动构造 与 拷贝构造的比较移动赋值 和 拷贝赋值 应用场景 定义 移动构造&#xff08;Move Constructor&#xff09;是一种特殊的构造函数&#xff0c;它通过接收一个右值引用参数来创建新对象&#xff0c;并从传入的对象中“移动…

操作系统——Windows 线程的互斥与同步

一、实验题目 Windows 线程的互斥与同步 二、实验目的 (1) 回顾操作系统进程、线程的有关概念&#xff0c;加深对 Windows 线程的理解。 (2) 了解互斥体对象&#xff0c;利用互斥与同步操作编写生产者-消费者问题的并发程序&#xff0c;加深对 P (即 semWait)、V(即 semSig…

[Spec] WiFi P2P Discovery

学习资料&#xff1a;Android Miracast 投屏 目录 学习资料&#xff1a;Android Miracast 投屏 P2P discovery Introduction Device Discovery procedures Listen State Search State Scan Phase Find Phase 总结 P2P discovery Introduction P2P发现使P2P设备能够快速…

WiSA Technologies开始接受WiSA E多声道音频开发套件的预订

美国俄勒冈州比弗顿市 — 2023年6月13日 — 为智能设备和下一代家庭娱乐系统提供沉浸式无线声效技术的领先供应商WiSA Technologies股份有限公司&#xff08;NASDAQ股票代码&#xff1a;WISA&#xff09;宣布&#xff1a;该公司现在正在接受其WiSA E开发套件的预订。WiSA E使用…

论文不详细解读(一)——MoCo系列

1. MoCo v1 论文名称&#xff1a; Momentum Contrast for Unsupervised Visual Representation Learning 开源地址&#xff1a;https://github.com/facebookresearch/moco 大佬详细解读&#xff1a;https://zhuanlan.zhihu.com/p/382763210 motivation 原始的端到端自监督方…

听说软件测试岗位基本都是女孩子在做?

“听我一朋友说&#xff0c;测试岗位基本都是女孩子做。” 不知道是不是以前“软件测试岗”给人印象是“不需要太多技术含量”的错觉&#xff0c;从而大部分外行认为从业软件测试的人员中女生应占了大多数。比如有人就觉得&#xff1a;软件测试主要是细心活&#xff0c;所以女生…

Python多任务执行方式

一、多任务的执行方式 并发&#xff1a;在一段时间内交替去执行任务&#xff08;单核CPU&#xff09;并行&#xff1a;CPU核数大于任务数 二、进程&#xff08;实现多任务&#xff09;——操作系统调度 进程是操作系统进行资源分配的基本单元一个程序至少有一个进程&#xf…

极致呈现系列之:EchartsK线图的数据量化

目录 什么是K线图K线图的特性及应用场景K线图的特性K线图的应用场景 Echarts中K线图的常用属性Vue3中创建K线图 什么是K线图 K线图是一种用于展示金融市场中股票、期货、外汇等交易品种价格走势的图表形式。它由一根根的垂直线条和水平线组成&#xff0c;能够直观地显示出一段…

OJ #378 字符串括号匹配2

题目描述 ​ 给出一个字符串&#xff0c;判断其中的左右括号是否匹配。 ​ 注&#xff1a;需同时判断左右圆括号 ( 和 ) &#xff0c;左右中括号 [和]&#xff0c;左右大括号 {和}。 ​ 不需要考虑括号之间的优先级的问题&#xff0c;也就是说&#xff0c;小括号包含大括号&…