线程同步——互斥锁

news2025/4/2 16:51:22

线程同步——互斥锁

目录

一、基本概念

二、打印成对出现的字母

 三、生产者消费者(有限缓冲问题)

3.1 基本概念

3.2 代码实现 


一、基本概念

互斥锁是一种用于控制对共享资源访问的同步机制。它确保在同一时间内,只有一个线程可以访问被保护的资源。互斥锁有两个基本操作:

  1. 加锁(Lock):当一个线程需要访问共享资源时,它会尝试获取互斥锁。如果锁是可用的(即未被其他线程持有),则该线程成功获取锁并进入临界区(Critical Section),执行对共享资源的操作。如果锁已经被其他线程持有,则该线程会被阻塞,直到锁被释放。

  2. 解锁(Unlock):当线程完成对共享资源的操作后,它会释放互斥锁,允许其他等待的线程获取锁并访问共享资源。

二、打印成对出现的字母

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

//定义了一个全局互斥锁mutex,用于同步线程对共享资源的访问。
pthread_mutex_t mutex;

//锁的使用在funa,funb 中
void* funa(void* arg)
{
    for(int i=0;i<5;i++)
    {
        printf("A");
        fflush(stdout);
        int n=rand()%3;
        sleep(n);
        printf("A");//第二次打印代表对打印机的使用结束
        fflush(stdout);
        n=rand()%3;
        sleep(n);
    }
}

void* funb(void* arg)
{
    for(int i=0;i<5;i++)
    {
        printf("B");
        fflush(stdout);
        int n=rand()%3;
        sleep(n);
        printf("B");//第二次打印代表对打印机的使用结束
        fflush(stdout);
        n=rand()%3;
        sleep(n);
    }
}

int main()
{
    //初始化全局互斥锁mutex,第二个参数为NULL表示使用默认属性。
    pthread_mutex_init(&mutex,NULL);//锁的地址,锁的属性
    //启动俩个线程
    pthread_t id1,id2;
    pthread_create(&id1,NULL,funa,NULL);
    pthread_create(&id2,NULL,funb,NULL);
    //等待线程结束
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    //销毁,锁变量的地址
    pthread_mutex_destroy(&mutex);
}

这个输出结果表明程序中存在线程同步问题,导致输出结果并不是预期的AABB

定义了一个全局互斥锁mutex,用于同步线程对共享资源的访问。

输出预期值AABBAABB 成对出现

需要加锁

prhread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);

 三、生产者消费者(有限缓冲问题)

3.1 基本概念

俩个或更多的线程共享同一个缓冲区,其中一个或多个线程作为“生产者”会不断的向缓冲区中添加数据,另一个或多个线程作为消费者从缓冲区中取走数据。

生产者和消费者必须互斥的使用缓冲区

缓冲区空时,消费者不能读取数据

缓冲区满时,生产者不能添加数据

s1 代表空闲空间有多少个 s2代表数据有多少个

如果在ps1之前加,那么所有人都不能操作缓冲区了 。先看有空间可不可以写入,有空间再加锁,否则加锁后操作不了缓冲区了。只要数据写入之后就立刻解锁释放

3.2 代码实现 

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

#define BUF_SIZE 30
pthread_mutex_t mutex;
sem_t sem_full;//信号量
sem_t sem_empty;

int in=0;//在那个数据可以写入数据
int out=0;//在那个可以读取
int buff[BUF_SIZE];//缓冲区
void* sc_fun(void* arg)
{
    for(int i=0;i<30;i++)
    {
        sem_wait(&sem_empty);
        pthread_mutex_lock(&mutex);
        buff[in]=rand()%100;
        printf("生产者在%d位置产生数据:%d\n",in,buff[in]);
        in=(in+1)%BUF_SIZE;
        pthread_mutex_unlock(&mutex);
        sem_post(&sem_full);

        int n=rand()%3;
        sleep(n);
    }
}

void* xf_fun(void* arg)
{
    for(int i=0;i<20;i++)
    {
        sem_wait(&sem_full);
        pthread_mutex_lock(&mutex);
        printf("----------消费者在%d位置消费数据:%d\n",out,buff[out]);
        out=(out+1)%BUF_SIZE;
        pthread_mutex_unlock(&mutex);
        sem_post(&sem_empty);//让空闲格子加1

        int n=rand()%3;
        sleep(n);
    }
}

int main()
{
    pthread_mutex_init(&mutex,NULL);
    sem_init(&sem_full,0,0);//信号量初始化
    sem_init(&sem_empty,0,BUF_SIZE);//为空的有多少个

    pthread_t sc_id[2];
    pthread_t xf_id[3];

    for(int i=0;i<2;i++)
    {
        pthread_create(&sc_id[i],NULL,sc_fun,NULL);
    }

    for(int i=0;i<3;i++)
    {
        pthread_create(&xf_id[i],NULL,xf_fun,NULL);
    }

    for(int i=0;i<2;i++)
    {
        pthread_join(sc_id[i],NULL);
    }

    for(int i=0;i<3;i++)
    {
        pthread_join(xf_id[i],NULL);
    }

    pthread_mutex_destroy(&mutex);
    sem_destroy(&sem_full);
    sem_destroy(&sem_empty);

    exit(0);
}
  • sem_fullsem_empty 是信号量,分别表示缓冲区中已占用的位置数和空闲位置数。

  • inout 分别表示下一个可写和可读的位置。

  • buff 是缓冲区数组,用于存储数据。

  • 输出中可以看到生产者和消费者线程交替进行操作,这表明它们在并发环境中工作。

  • 生产者在生产数据前会等待一个空闲位置(sem_wait(&sem_empty)),然后锁定互斥锁,生产数据,解锁互斥锁,并释放一个已占用位置(sem_post(&sem_full))。

  • 消费者在消费数据前会等待一个已占用位置(sem_wait(&sem_full)),然后锁定互斥锁,消费数据,解锁互斥锁,并释放一个空闲位置(sem_post(&sem_empty))。

  • 已占用:表示该位置已经被生产者放入了数据,并且这些数据尚未被消费者取出。缓冲区中已经被生产者放入数据的位置。缓冲区可以被看作是一个固定大小的数组,用于临时存储生产者生成的数据,直到消费者将其取出。

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

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

相关文章

C#实现HTTP服务器:处理文件上传---解析MultipartFormDataContent

完整项目托管地址&#xff1a;https://github.com/sometiny/http HTTP还有重要的一块&#xff1a;文件上传。 这篇文章将详细讲解下&#xff0c;前面实现了同一个链接处理多个请求&#xff0c;为了方便&#xff0c;我们独立写了一个HTTP基类&#xff0c;专门处理HTTP请求。 ht…

leetcoed0044. 通配符匹配 hard

1 题目&#xff1a;通配符匹配 官方难度&#xff1a;难 给你一个输入字符串 (s) 和一个字符模式 ( p ) &#xff0c;请你实现一个支持 ‘?’ 和 ‘*’ 匹配规则的通配符匹配&#xff1a; ‘?’ 可以匹配任何单个字符。 ‘*’ 可以匹配任意字符序列&#xff08;包括空字符序…

蓝桥杯嵌入式第十二届程序设计题

一、题目概览 设计一个小型停车计费系统 二、分模块实现 1、LCD void disp_proc() {if(view0){char text[30];sprintf(text," Data");LCD_DisplayStringLine(Line2,(uint8_t *)text);sprintf(text," CNBR:%d ",Cnum);LCD_DisplayStri…

python多态、静态方法和类方法

目录 一、多态 二、静态方法 三、类方法 一、多态 多态&#xff08;polymorphism&#xff09;是面向对象编程中的一个重要概念&#xff0c;指的是同样的方法调用可以在不同的对象上产生不同的行为。在Python中&#xff0c;多态是通过方法的重写&#xff08;override&#x…

DTMF从2833到inband的方案

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 之前的文章中介绍过通过dialplan拨号计划配置的方法&#xff0c;实现2833到inband的转换&#xff0c;但是实际生产环境中的场景会更复杂&#xff0c;无法预先在dialplan中设置好相关参数和函数。 环境 CentOS 7.9 fr…

在Vue 3 + TypeScript + Vite 项目中安装和使用 SCSS

在Vue 3 TypeScript Vite 项目中安装和使用 SCSS 1、安装 SCSS 的相关依赖 npm install sass --save-dev2、配置 Vite 对于 Vue 3&#xff0c;Vite 已经内置了对 SCSS 的支持&#xff0c;通常不需要额外的配置。但是&#xff0c;如果需要自定义配置&#xff0c;可以在路径…

Uni-app入门到精通:tabBar节点实现多页面的切换

tabBar节点用于实现多页面的切换。对于一个多tabBar应用&#xff0c;可以通过tabBar节点配置项指定一级导航栏&#xff0c;以及tabBar切换时显示的对应页面。在pages.json中提供tabBar节点配置&#xff0c;不仅是为了方便快速开发导航&#xff0c;更重要的是提示App平台和小程序…

运筹说 第134期 | 矩阵对策的解法

上一期我们了解了矩阵对策的基本理论&#xff0c;包含矩阵对策的纯策略、矩阵对策的混合策略和矩阵对策的基本定理。 接下来小编将为大家介绍矩阵对策的解法&#xff0c;包括图解法、方程组法和线性规划法三种经典方法。 01 图解法 本节首先介绍矩阵对策的图解法&#xff0c;…

3. 轴指令(omron 机器自动化控制器)——>MC_CamOut

机器自动化控制器——第三章 轴指令 15 MC_CamOut变量▶输入变量▶输出变量▶输入输出变量 功能说明▶时序图▶指令的中止▶重启运动指令▶多重启动运动指令▶异常 MC_CamOut 结束通过输入参数指定的轴的凸轮动作 指令名称FB/FUN图形表现ST表现MC_CamOut解除凸轮动作FBMC_Cam…

TF32 与 FP32 的区别

TF32&#xff08;Tensor Float 32&#xff09;与FP32&#xff08;单精度浮点数&#xff09;是两种用于深度学习和高性能计算的浮点格式&#xff0c;其核心区别体现在精度、性能优化和应用场景上。以下是两者的详细对比分析&#xff1a; 一、位宽与结构差异 FP32的位宽结构 FP32…

【大模型】视觉语言模型:Qwen2.5-VL的使用

官方github地址&#xff1a;https://github.com/QwenLM/Qwen2.5-VL 目录 Qwen家族的最新成员&#xff1a;Qwen2.5-VL 主要增强功能 模型架构更新 快速开始 使用Transformers聊天 Docker Qwen家族的最新成员&#xff1a;Qwen2.5-VL 主要增强功能 强大的文档解析功能&am…

测试用例与需求脱节的修复方案

测试用例与需求脱节的问题可通过明确需求定义、加强需求追踪、建立有效沟通机制进行修复。其中&#xff0c;加强需求追踪尤为关键&#xff0c;能确保测试用例与实际需求的精确匹配&#xff0c;避免资源浪费和测试效果不佳。据行业研究&#xff0c;约70%的软件缺陷源于需求管理不…

【Unity】 鼠标拖动物体移动速度跟不上鼠标,会掉落

错误示范&#xff1a; 一开始把移动的代码写到update里去了&#xff0c;发现物体老是掉(总之移动非常不流畅&#xff0c;体验感很差&#xff09; void Update(){Ray ray Camera.main.ScreenPointToRay(Input.mousePosition);if (Physics.Raycast(ray, out RaycastHit hit, M…

VLAN 高级特性

VLAN Access 类型端口&#xff1a;只能属于 1 个 VLAN&#xff0c;发出数据时只能根据 PVID 剥离一个 VLAN Tag 入方向&#xff1a;针对没有 tag 的数据包打上 PVID 的 tag出方向&#xff1a;将 tag 为本接口 PVID 的数据包去掉 tag&#xff0c;发出数据。&#xff08;只有在与…

学习中学习的小tips(主要是学习苍穹外卖的一些学习)

目录 架构的细分 使用实体类来接收配置文件中的值 webMvcConfig类&#xff1a; jwt令牌 管理端的拦截器&#xff1a; JwtProperties&#xff1a; JwtTokenAdminInterceptor &#xff1a; 对密码加密操作 Redis&#xff1a; 分页查询 整体思想 为什么动态 SQL 推荐传实体…

【极速版 -- 大模型入门到进阶】LORA:大模型轻量级微调

文章目录 &#x1f30a; 有没有低成本的方法微调大模型&#xff1f;&#x1f30a; LoRA 的核心思想&#x1f30a; LoRA 的初始化和 r r r 的值设定&#x1f30a; LoRA 实战&#xff1a;LoraConfig参数详解 论文指路&#xff1a;LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE M…

线程同步——读写锁

Linux——线程同步 读写锁 目录 一、基本概念 1.1 读写锁的基本概念 1.2 读写锁的优点 1.3 读写锁的实现 1.4 代码实现 一、基本概念 线程同步中的读写锁&#xff08;Read-Write Lock&#xff09;&#xff0c;也常被称为共享-独占锁&#xff08;Shared-Exclusive Lock&a…

邪性!Anaconda安装避坑细节Windows11

#工作记录 最近不断重置系统和重装Anaconda&#xff0c;配置的要累死&#xff0c;经几十次意料之外的配置状况打击之后&#xff0c;最后发现是要在在Anaconda安装时&#xff0c;一定要选“仅为我安装”这个选项&#xff0c;而不要选“为所有用户安装”这个选项。 选“仅为我安…

【大模型】激活函数之SwiGLU详解

文章目录 1. Swish基本定义主要特点代码实现 2. GLU (Gated Linear Unit)基本定义主要特点代码实现 3. SwiGLU基本定义主要特点代码实现 参考资料 SWiGLU是大模型常用的激活函数&#xff0c;是2020年谷歌提出的激活函数&#xff0c;它结合了Swish和GLU两者的特点。SwiGLU激活函…

AOA与TOA混合定位,MATLAB例程,三维空间下的运动轨迹,滤波使用EKF,附下载链接

本文介绍一个MATLAB代码&#xff0c;实现基于 到达角&#xff08;AOA&#xff09; 和 到达时间&#xff08;TOA&#xff09; 的混合定位算法&#xff0c;结合 扩展卡尔曼滤波&#xff08;EKF&#xff09; 对三维运动目标的轨迹进行滤波优化。代码通过模拟动态目标与基站网络&am…