Linux-线程同步(条件变量、POSIX信号量)

ops/2024/9/24 10:39:17/

一、线程同步概念

        线程同步(Thread Synchronization)是多线程编程中的一个重要概念,它指的是在多线程环境中,各个线程按照一定的顺序或规则来执行,以确保数据的完整性和一致性,避免数据竞争(Data Race)和条件竞争(Race Condition)等问题。

        在多线程程序中,不同的线程可能会同时访问和修改共享资源(如内存中的变量、文件、数据库等)。如果没有适当的同步机制,就可能发生数据不一致或数据损坏的情况,因为线程的执行顺序和速度是不可预测的。

线程同步的目的主要是:

  1. 保护共享资源:确保在同一时刻只有一个线程可以访问或修改某个特定的共享资源。
  2. 维护数据一致性:通过控制对共享资源的访问顺序,确保数据在多个线程之间保持一致性和正确性。
  3. 协调线程的执行:按照预定的顺序或规则来执行线程,以避免出现不可预测的行为或结果。

实现线程同步的方法有多种,包括但不限于以下几种:

  • 互斥锁(Mutex):互斥锁是一种最基本的同步机制,用于保护共享资源,确保同一时刻只有一个线程可以访问该资源。当一个线程访问共享资源时,它会先锁定互斥锁,访问结束后释放锁,其他线程才能访问该资源。
  • 信号量(Semaphore):信号量是一种更高级的同步机制,用于控制多个线程对多个共享资源的访问。信号量本质是一个计数器,表示可用资源的数量,是一种可用资源的预订机制。线程在访问资源前,会先尝试减少信号量的计数器,如果计数器大于0,则允许访问;否则,线程将被阻塞,直到计数器大于0为止。
  • 条件变量(Condition Variable):条件变量与互斥锁一起使用,用于线程间的同步。线程在特定条件下会被阻塞,直到另一个线程改变条件并通知它。

二、条件变量

        当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。 例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中,但是如果访问的这个线程竞争锁的能力比较强,他会一直申请锁--判断是否为空--释放锁,重复执行,而向队列添加数据的线程反而因为竞争不到锁导致程序无法合理执行(互斥锁只能保证同一时刻只有一个线程访问共享资源但不能保证共享资源被使用的合理性),这种情况就需要用到条件变量。

条件变量函数

1.初始化(两种方式)

方法一:静态分配

//当条件变量定义为全局的时候可以这样初始化
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;  

方法二:动态分配 

int pthread_cond_init(pthread_cond_t *restrict cond,const
pthread_condattr_t *restrict attr);
参数:
cond:要初始化的条件变量
attr:NULL

2.销毁

int pthread_cond_destroy(pthread_cond_t *cond)

3.等待条件满足

int pthread_cond_wait(pthread_cond_t *restrict
cond,pthread_mutex_t *restrict mutex);
参数:
cond:要在这个条件变量上等待
mutex:互斥量
//成功返回0,失败返回错误码.

条件等待必须与互斥锁同时使用,一般的使用逻辑是先申请锁,在判断条件,如果条件满足,就开始等待,由于等待时该线程是申请到锁的,这个阶段其他线程就无法访问共享资源了,所以等待时会先将锁释放掉,当收到唤醒信号时在重新竞争锁。

问题:为什么 pthread_cond_wait 需要互斥量?

  • 条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,一直等 下去都不会满足,所以必须要有一个线程通过某些操作,改变共享变量,使原先不 满足的条件变得满足,并且友好的通知等待在条件变量上的线程。
  • 条件不会无缘无故的突然变得满足了,必然会牵扯到共享数据的变化。所以一 定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据
pthread_mutex_lock();while (condition_is_false)
{pthread_cond_wait();
}pthread_mutex_unlock();

ps、一般判断条件不用 if 而是用 while ,因为假设有个线程发送信号唤醒了两个线程,有一个线程竞争到锁了向下继续执行了,可能此时条件又不满足了,改线程虽然唤醒了但是不能竞争锁,还需要继续等待下一次的唤醒信号才能重新竞争锁,所以一般循环判断条件

唤醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

三、POSIX 信号量

信号量回顾

信号量的本质是一个计数器,用于标识共享资源的数量,申请信号量的本质是对共享资源的预定机制,例如许多人想去电影院看电影,影院先不管你在影厅里面干什么,你想进到影厅首先需要先买票,票数其实就相当于信号量,买票就相当于预定座位,当信号量被申请完,其他人还想进影厅就得等待信号量,等其他人退票你申请到信号量才能进来

函数介绍

初始化信号量

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
pshared:0 表示线程间共享,非零表示进程间共享
value:信号量初始值,共享资源个数

销毁信号量

int sem_destroy(sem_t *sem);

等待信号量

int sem_wait(sem_t *sem); //P()
功能:等待信号量,会将信号量的值减 1

发布信号量

int sem_post(sem_t *sem);//V()
功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加 1。

对于条件变量和POSIX信号量的理解,大家可以参考:张得帅c/Linux

  • 基于BlockQueue的生产消费模型
  • 基于环形队列的生产消费模型


http://www.ppmy.cn/ops/56743.html

相关文章

SpringSecurity中文文档(Servlet OAuth 2.0 Resource Server)

OAuth 2.0 Resource Server Spring Security 通过使用两种形式的 OAuth 2.0无记名令牌来支持对端点的保护: JWTOpaque Tokens 在应用程序将其权限管理委托给授权服务器(例如 Okta 或 Ping Identity)的情况下&#xff0c;这非常方便。资源服务器可以查询此授权服务器来对请求…

未来互联网的新篇章:深度解析Facebook的技术与战略

随着科技的飞速发展和社会的不断变迁&#xff0c;互联网作为全球信息交流的重要平台&#xff0c;正经历着前所未有的变革和演进。作为全球最大的社交媒体平台之一&#xff0c;Facebook不仅是人们沟通、分享和互动的重要场所&#xff0c;更是科技创新和数字化进程的推动者。本文…

Perl基础入门指南:从零开始掌握Perl编程

Perl是一种功能强大且灵活的编程语言&#xff0c;广泛应用于系统管理、Web开发、网络编程和文本处理等领域。如果你是编程新手或者想学习一种新的编程语言&#xff0c;Perl是一个不错的选择。本文将带你了解Perl的基础知识&#xff0c;并通过简单的示例代码帮助你快速入门。 什…

RightFont 8.7.0 Mac专业字体管理工具

RightFont 适用于 macOS 的终极字体管理器应用程序&#xff0c;提供无缝的字体管理体验。它结合了速度、直观的功能和专业的功能&#xff0c;使用户能够轻松预览、安装、组织和共享字体。 RightFont 8.7.0 Mac下载 RightFont 8.0的新增功能 RightFont 8.0 带来了全新的智能选…

docker 安装orcale11数据库

初次安装流程 1.拉取 oracle 11g 镜像&#xff08;有6g&#xff0c;要等较长的时间&#xff09;: docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g2.执行以下命令以创建并运行 Oracle 11g 容器&#xff08;其中&#xff1a;30026为映射主机端口&#xff0…

【零基础】学JS之APIS

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

论文阅读:A Survey on Evaluation of Large Language Models

A Survey on Evaluation of Large Language Models 这篇论文是由Yupeng Chang等人撰写的关于大型语言模型&#xff08;LLMs&#xff09;评估的综述&#xff0c;题为《A Survey on Evaluation of Large Language Models》。 摘要 大型语言模型&#xff08;LLMs&#xff09;在…

qt 用代码添加一个控件

在 Qt 中使用代码添加一个控件到窗口或另一个控件上是一个常见的操作。以下是一个简单的例子&#xff0c;演示了如何在 Qt 的主窗口&#xff08;通常是一个继承自 QMainWindow 或 QWidget 的类&#xff09;中使用代码添加一个 QPushButton 控件&#xff1a; 包含必要的头文件&…