LinuxC语言线程的同步与互斥

一.线程的同步与互斥
1. 基础概念:
1.1 互斥:对共享资源的访问,同一时刻只允许一个访问者进行访问,互斥具有唯一和排他性,
互斥无法保证对共享资源的访问顺序
1.2 同步: 在互斥的基础上,实现对共享资源的有序访问。

 2. 互斥问题的解决方案:
2.1  互斥锁(mutex)
互斥锁机制: 互斥锁机制是通过对内核提供的互斥锁进行上锁来实现对共享资源的受控访问。
在共享资源访问前,线程对互斥锁进行竞争上锁,对互斥锁上锁的线程可以对
共享资源进行独占式访问,其他线程阻塞等待,直到上锁的线程对互斥锁进行了
解锁操作。线程再次进行竞争上锁。
互斥锁的操作:
1)  申请初始化互斥锁
静态初始化:  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态初始化: pthread_mutex_init

                函数头文件    #include <pthread.h> 
函数原型:     int pthread_mutex_init(pthread_mutex_t* mutex,
const pthread_mutexattr_t *attr);
函数功能:    申请初始化互斥锁
函数参数:    [OUT] mutex: 待初始化的互斥锁。
attr: 互斥锁属性:如果为NULL,默认属性为快速互斥锁。
attr 取值可以为以下3种:
PTHREAD_MUTEX_INITIALIZER:  快速互斥锁
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_UP:  递归互斥锁
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_UP:  检错互斥锁

            函数返回值:  成功返回  0
失败返回错误码
2)  互斥锁上锁
pthread_mutex_lock 
函数头文件    #include <pthread.h> 
函数原型:     int pthread_mutex_lock(pthread_mutex_t* mutex);
函数功能:    互斥锁上锁
函数参数:    mutex: 待上锁的互斥锁。
函数返回值:  成功返回  0
失败返回错误码
3)  互斥锁上锁
pthread_mutex_unlock 
函数头文件    #include <pthread.h> 
函数原型:     int pthread_mutex_unlock(pthread_mutex_t* mutex);
函数功能:    互斥锁上锁
函数参数:    mutex: 待解锁的互斥锁。
函数返回值:  成功返回  0
失败返回错误码
4)  互斥锁回收
pthread_mutex_destroy 
函数头文件    #include <pthread.h> 
函数原型:     int pthread_mutex_destroy(pthread_mutex_t* mutex);
函数功能:    互斥锁回收
函数参数:    mutex: 待回收的互斥锁。
函数返回值:  成功返回  0
失败返回错误码

                 
2.2 读写锁(rwlock)

        读写锁机制:   读写锁与互斥锁的机制类似,不同之处在于读写锁存在读上锁和写上锁两种上锁方式,
读写锁有3种状态,分别为读上锁,写上锁,解锁,如果基于以下使用规则:

               1. 写上锁仅能在读写锁处于解锁状态才能进行,否则写上锁的线程阻塞;
2. 读上锁仅能在读写锁未处于写上锁状态才能进行,否则读上锁的线程阻塞;
对一个已经读上锁的读写锁再次读上锁,线程不会阻塞。

应用场景:   读访问多于写访问的情况。
实际使用:   若线程对共享资源是读访问,则在共享资源访问前,对读写锁进行读上锁,
否则对读写锁进行写上锁

    读写锁的基本操作:
1)  申请初始化读写锁
静态初始化:  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
动态初始化: pthread_rwlock_init

                函数头文件    #include <pthread.h> 
函数原型:     int pthread_rwlock_init(pthread_rwlock_t* rwlock,
const pthread_rwlockattr_t *attr);
函数功能:    申请初始化读写锁
函数参数:    [OUT] rwlock: 待初始化的读写锁。
attr: 读写锁属性:一般为NULL。

函数返回值:  成功返回  0
失败返回错误码
2)  读写锁读上锁
pthread_rwlock_lock 
函数头文件    #include <pthread.h> 
函数原型:     int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
函数功能:    读写锁读上锁
函数参数:    rwlock: 待上锁的读写锁。
函数返回值:  成功返回  0
失败返回错误码
3)  读写锁写上锁
pthread_rwlock_wrlock 
函数头文件    #include <pthread.h> 
函数原型:     int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
函数功能:    读写锁写上锁
函数参数:    rwlock: 待上锁的读写锁。
函数返回值:  成功返回  0
失败返回错误码
4)  读写锁解锁
pthread_rwlock_unlock 
函数头文件    #include <pthread.h> 
函数原型:     int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);
函数功能:    读写锁解锁
函数参数:    rwlock: 待解锁的读写锁。
函数返回值:  成功返回  0
失败返回错误码
5)  读写锁回收
pthread_rwlock_destroy 
函数头文件    #include <pthread.h> 
函数原型:     int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);
函数功能:    读写锁回收
函数参数:    rwlock: 待回收的读写锁。
函数返回值:  成功返回  0
失败返回错误码   
2.3 信号量(semaphore) 

多线程环境下的共享资源互斥或同步访问,往往是使用POSIX标准的信号量

        应用场景:   共享资源总数大于1。

    POSIX标准的信号量:
1.  有名信号量  (进程间)
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);

         例子:sem_t *s =  sem_open("/mysem",O_RDWR|O_CREAT,0644,1)  ;
2.  无名信号量  (线程间)
sem_init
函数头文件    #include <semaphore.h> 
函数原型:     int sem_init(sen_t* sem,int pshared,unsigned int value);
函数功能:    申请初始化信号量
函数参数:    sem: 待初始化的无名信号量。
pshared:信号量的共享方式,0 代表同一进程的不同线程间共享
value: 设置给信号量的初始值
函数返回值:  成功返回  0
失败返回  -1 错误码放在errno

     信号量操作:
sem_wait
函数头文件     #include <semaphore.h>
函数原型:     int sem_wait(sen_t* sem);
函数功能:    信号量P操作
函数参数:    sem: 待操作的无名信号量。
函数返回值:  成功返回  0
失败返回  -1 错误码放在errno

          sem_post
函数头文件     #include <semaphore.h>
函数原型:     int sem_post(sen_t* sem);
函数功能:    信号量V操作
函数参数:    sem: 待操作的无名信号量。
函数返回值:  成功返回  0
失败返回  -1 错误码放在errno

         sem_destroy
函数头文件     #include <semaphore.h>
函数原型:     int sem_destroy(sen_t* sem);
函数功能:    回收信号量
函数参数:    sem: 待操作的无名信号量。
函数返回值:  成功返回  0
失败返回  -1 错误码放在errno

3. 同步问题的解决方案:

     3.1  条件变量
条件变量机制:  条件变量主要是通过主动阻塞线程,等待某个条件发生,条件成立后,
再执行相关的资源访问动作。

               条件变量必须搭配互斥锁一起使用,原因是条件的判断必须在互斥锁的保护下进行。
被条件变量阻塞的线程,可以通过对条件变量发送信号的方式来唤醒,被唤醒的线程
获得互斥锁,并重新判断条件。

       条件变量使用场景: 对共享资源有条件访问。

       条件变量的基础操作:
1)  申请初始化条件变量
静态初始化:  pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态初始化: pthread_cond_init

                函数头文件    #include <pthread.h> 
函数原型:     int pthread_cond_init(pthread_cond_t* cond,
const pthread_condattr_t *attr);
函数功能:    申请初始化读写锁
函数参数:    [OUT] cond: 待初始化的条件变量。
attr:条件变量属性:一般为NULL。

函数返回值:  成功返回  0
失败返回错误码

          2)  用条件变量阻塞线程                

                函数头文件    #include <pthread.h> 
函数原型:     int pthread_cond_wait(pthread_cond_t* cond,
pthread_mutex_t *mutex);
函数功能:    用条件变量阻塞线程,同时解锁互斥锁
函数参数:    cond: 待操作的条件变量。
mutex:待解锁的互斥锁。

函数返回值:  成功返回  0
失败返回错误码 

3)  给条件变量发送通知唤醒阻塞线程                

                函数头文件    #include <pthread.h> 
函数原型:     int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);
函数功能:    给条件变量发送通知唤醒阻塞线程
函数参数:    cond: 待操作的条件变量。                    
函数返回值:  成功返回  0
失败返回错误码 

         4)  回收条件变量                

                函数头文件    #include <pthread.h> 
函数原型:     int pthread_cond_destroy(pthread_cond_t* cond);
函数功能:    回收条件变量
函数参数:    cond: 待操作的条件变量。                      

函数返回值:  成功返回  0
失败返回错误码 

3.2  信号量 

信号量用于解决同步问题: 往往需要多个信号量,将其中的一个信号初值设为资源总数,其余设为0,
如果想让某个线程先执行共享资源访问,则该线程中访问共享资源前,对
初值非0的信号量做P操作,资源访问结束后,对初值为0的信号量做V操作。
后对共享资源访问的线程,访问共享资源前,对初值为0的信号量做P操作,
资源访问结束后,对初值非0的信号量做V操作。

       信号量典型同步问题--生产者消费者问题:

生产者消费者问题: 也称为有限缓冲问题:
该问题描述了两个线程(生产者/消费者)对共享缓冲区的访问,生产者是向缓冲区
中写入数据,消费者是读取数据。

                   该问题的核心是: 保证生产者线程在共享缓冲区满容量时不允许访问,同时消费者
在共享缓冲区空容量时不允许访问。

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

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

相关文章

Centos 7.6离线安装docker

在内网环境下&#xff0c;一般不能联网在线部署&#xff0c;这时候就需要以离线的方式安装docker。本节内容主要总结一下在CentOS 7.6环境中离线安装docker的步骤。 1、下载docker安装包 https://pan.baidu.com/share/init?surlPaUllQZ-dwpgJ7quA5IkcQ&pwd4sfc 2、上传到服…

生成式推荐模型的长序列特征:离线存储

文章目录长序列特征的例子1. Event-level features2. Sequence-level featuresAggregation FeaturesSession-based FeaturesTemporal Order Features3. User-level features4. Interaction features (between user and item/context)how to store the long term user behaviro …

Linux inode 实现机制深入分析

Linux inode 实现机制深入分析 1 Inode 基本概念与作用 Inode&#xff08;Index Node&#xff09;是 Linux 和其他类 Unix 操作系统中文件系统的核心数据结构&#xff0c;用于存储文件或目录的元数据&#xff08;metadata&#xff09;。每个文件或目录都有一个唯一的 inode&…

Flask 之请求钩子详解:掌控请求生命周期

在构建现代 Web 应用时&#xff0c;我们常常需要在请求的不同阶段自动执行一些通用逻辑&#xff0c;例如&#xff1a;记录日志、验证权限、连接数据库、压缩响应、添加安全头等。如果在每个视图函数中重复这些代码&#xff0c;不仅冗余&#xff0c;而且难以维护。Flask 请求钩子…

设计模式七大原则附C++正反例源码

设计模式的七大原则是软件设计的基石,它们指导开发者构建高内聚、低耦合、易维护、可扩展的系统。以下以C++为例,详细介绍这七大原则: 一、单一职责原则(Single Responsibility Principle, SRP) 定义:一个类应该只有一个引起它变化的原因(即一个类只负责一项职责)。 …

云计算之中间件与数据库

一、云数据库的特性云数据库是指被优化或部署到一个虚拟计算环境中的数据库&#xff0c;可以实现按需付费、按需扩展、高可用性以及存储整合等优势。根据数据库类型一般分为关系型数据库和非关系型数据库&#xff08;NoSQL数据库&#xff09; 。云数据库的特性序号云数据库的特…

codeforces(1045)(div2) E. Power Boxes

E.电源箱 每次测试时限&#xff1a; 2 秒 每次测试的内存限制&#xff1a;256 兆字节 输入&#xff1a;标准输入 输出&#xff1a;标准输出 这是一个互动问题。 给你 nnn 个方格&#xff0c;索引从 111 到 nnn 。这些方格看起来完全相同&#xff0c;但是每个方格都有一个隐藏的…

4G模块 EC200通过MQTT协议连接到阿里云

命令说明 基础AT指令ATI显示MT的ID信息ATCIMI查询IMSIATQCCID查询ICCIDATCSQ查询信号强度ATCGATT?查询当前PS域状态MQTT配置指令ATQMTCFG配置MQTT可选参数ATQMTCFG配置MQTT可选参数.ATQMTOPEN打开MQTT客户端网络ATQMTCLOSE关闭MQTT客户端网络ATQMTCONN连接客户端到MQTT服务器…

如何选择合适的安全监测预警系统

在当今高度复杂和互联的数字化时代&#xff0c;安全威胁无处不在且持续演变。一套高效、可靠的安全监测预警系统已成为组织保障其物理资产、数字信息和关键业务连续性的核心基础设施。然而&#xff0c;面对市场上琳琅满目的产品和解决方案&#xff0c;如何做出符合自身需求的选…

ELK-使用logstash-output-zabbix插件实现日志通过zabbix告警

ELK-使用logstash-output-zabbix插件实现日志通过zabbix告警logstash-output-zabbix插件安装编辑logstash配置文件在zabbix上创建模板实现的效果:elk收集上来的日志中含有报错时(例如error等)&#xff0c;logstash过滤出来将这部分日志打到zabbix&#xff0c;再通过zabbix结合钉…

【C++游记】物种多样——谓之多态

枫の个人主页 你不能改变过去&#xff0c;但你可以改变未来 算法/C/数据结构/C Hello&#xff0c;这里是小枫。C语言与数据结构和算法初阶两个板块都更新完毕&#xff0c;我们继续来学习C的内容呀。C是接近底层有比较经典的语言&#xff0c;因此学习起来注定枯燥无味&#xf…

Visual Scope (Serial_Digital_Scope V2) “串口 + 虚拟示波器” 工具使用记录

VisualScope 就是一个 “串口 + 虚拟示波器” 的工具,适合在没有昂贵示波器/逻辑分析仪时做嵌入式调试。它的核心步骤就是 MCU 定时发数据 → PC 串口接收 → 软件画波形。 首先准备串口通信工具后,插入电脑,安装完USB转串口驱动后,在“我的电脑”-“设备及管理器”-“端口…

c++ 观察者模式 订阅发布架构

#include <iostream> #include <vector> #include <algorithm> #include <memory> #include <mutex>// 观察者接口 class IObserver { public:virtual ~IObserver() default;virtual void update(const std::string& message) 0; };// 主题…

oracle 表空间扩容(增加新的数据文件)

SELECT tablespace_name,file_name,ROUND(bytes / 1024 / 1024, 2) AS size_mb,ROUND(maxbytes / 1024 / 1024, 2) AS max_size_mb,status,autoextensible FROM dba_data_files ORDER BY tablespace_name;--给表空间增加一个新数据库文件ALTER TABLESPACE EAS_D_EAS_STANDARDAD…

DAY 58 经典时序预测模型2

知识点回顾&#xff1a; 时序建模的流程时序任务经典单变量数据集ARIMA&#xff08;p&#xff0c;d&#xff0c;q&#xff09;模型实战SARIMA摘要图的理解处理不平稳的2种差分 n阶差分---处理趋势季节性差分---处理季节性 昨天我们掌握了AR, MA, 和 ARMA 模型&#xff0c;它们…

【人工智能】AI代理重塑游戏世界:动态NPC带来的革命性沉浸式体验

还在为高昂的AI开发成本发愁?这本书教你如何在个人电脑上引爆DeepSeek的澎湃算力! 在当今游戏行业迅猛发展的时代,AI代理技术正悄然引发一场革命,尤其是动态非玩家角色(NPC)的应用,将传统静态游戏体验提升至全新的沉浸式境界。本文深入探讨AI代理在游戏中的核心作用,从…

服务器关机故障排查:大白话版笔记

注意:本文解释文字仅供学习交流使用,不构成专业的技术指导或建议;只是理论实例解释不代表实际运维场景操作,注意鉴别! 运维日常最头疼的就是服务器 “突然躺平” —— 要么没操作就自己关机,要么想关还关不掉。 紧急检查清单 (Cheat Sheet) 服务器突然宕机,重启后第一…

如何通过docker进行本地部署?

如何通过docker进行本地部署&#xff1f; 在做项目的过程中&#xff0c;想要上线项目的话肯定是不能在我们电脑上进行开发的&#xff0c;要部署到服务器上面&#xff0c;今天就总结一下操作步骤。 1、创建springboot项目 随便创建一个springboot工程&#xff0c;确保control…

解锁AI“黑匣”:监督、无监督与强化学习探秘

在当今数字化浪潮汹涌澎湃的时代&#xff0c;AI 决策已然成为推动各领域变革与发展的核心驱动力&#xff0c;从智能语音助手到自动驾驶汽车&#xff0c;从医疗诊断辅助到金融风险预测&#xff0c;AI 决策的身影无处不在&#xff0c;深刻地改变着人们的生活与工作方式。​AI 决策…