线程的同步

server/2024/10/21 9:40:59/

文章目录

  • 线程的同步
    • 同步:
    • 条件变量:
      • `pthread_cond_init()`:
      • `pthread_cond_wait()`
      • `pthread_cond_signal`
      • pthread_cond_broadcast
    • cp问题
        • 伪唤醒
    • 信号量
      • **多线程的互斥用信号量**:
      • **单线程的互斥用锁**:

线程的同步

同步:

让所有的线程获取锁,按照一定的顺序。按照一定的顺序获取资源就是同步。 (顺序性)

条件变量:

条件变量必须依赖于锁的使用

pthread_cond_init():

是用于初始化条件变量的函数,条件变量是多线程编程中的一种同步机制,允许线程在等待某个条件满足时进入休眠状态,并在条件满足时被唤醒。条件变量通常与互斥锁一起使用,以防止竞争条件。

函数原型:

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

参数

  • cond: 指向要初始化的条件变量的指针。
  • attr: 指向条件变量属性对象的指针。如果传递 NULL,则使用默认属性。

返回值

  • 成功时返回 0
  • 失败时返回一个错误码。

pthread_cond_wait()

是 POSIX 线程库中用于条件变量等待的函数。它使线程进入等待状态,直到某个条件满足。在此过程中,pthread_cond_wait() 会自动释放与条件变量关联的互斥锁,并在条件变量被唤醒时重新获取该互斥锁。这种行为可以防止等待条件的线程占用互斥锁,使得其他线程可以修改条件并唤醒等待线程。

函数原型

#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
  • cond: 指向条件变量的指针。
  • mutex: 指向与条件变量关联的互斥锁的指针。

返回值

  • 返回 0 表示成功。
  • 返回错误码表示失败(例如:EINVAL 表示无效的条件变量或互斥锁,EPERM 表示互斥锁未被调用线程持有)。

pthread_cond_signal

是 POSIX 线程库中用于条件变量的一个函数。它的作用是唤醒至少一个等待在指定条件变量上的线程。条件变量通常和互斥锁一起使用,以实现线程间的同步。

int pthread_cond_signal(pthread_cond_t *cond);
  • cond:指向条件变量的指针(pthread_cond_t 类型)。
  • 返回值:如果成功返回 0,如果失败返回错误代码。

pthread_cond_signal 唤醒一个(至少一个)等待在 cond 条件变量上的线程。如果有多个线程在这个条件变量上等待,则唤醒其中的一个线程。该函数不会确保唤醒哪个具体线程,这是由操作系统调度器决定的。

使用场景

条件变量通常用于线程之间的等待和通知机制。例如,生产者-消费者问题中,消费者可能在条件变量上等待,直到生产者生成了新数据并发出信号。

条件变量的典型使用流程

  1. 等待线程:通常使用 pthread_cond_waitpthread_cond_timedwait 函数等待某个条件。

    • 在调用 pthread_cond_wait 之前,必须持有与该条件变量关联的互斥锁(pthread_mutex_t)。
    • 当条件满足并从等待中返回时,线程会自动重新持有该互斥锁。
  2. 通知线程:当条件满足时,调用 pthread_cond_signal(唤醒一个线程)或者 pthread_cond_broadcast(唤醒所有等待的线程)来通知等待的线程。

例子

以下是一个简单的生产者-消费者模型的例子,展示了如何使用 pthread_cond_signal

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ready = 0; // 共享资源:表示是否有可供消费的数据void* consumer(void* arg) {pthread_mutex_lock(&mutex);// 等待数据准备好while (!ready) {printf("Consumer waiting for data...\n");pthread_cond_wait(&cond, &mutex); // 等待条件变量信号}printf("Consumer received signal and consumed data!\n");// 释放互斥锁pthread_mutex_unlock(&mutex);return NULL;
}void* producer(void* arg) {sleep(1); // 模拟生产者生成数据的过程pthread_mutex_lock(&mutex);ready = 1; // 生产者准备好数据printf("Producer produced data and sending signal...\n");pthread_cond_signal(&cond); // 发出条件信号,唤醒一个等待线程pthread_mutex_unlock(&mutex);return NULL;
}int main() {pthread_t producer_thread, consumer_thread;// 创建消费者和生产者线程pthread_create(&consumer_thread, NULL, consumer, NULL);pthread_create(&producer_thread, NULL, producer, NULL);// 等待线程完成pthread_join(consumer_thread, NULL);pthread_join(producer_thread, NULL);return 0;
}

消费者线程:进入 pthread_cond_wait 状态,等待条件变量上的信号。pthread_cond_wait 会自动释放 mutex,使其他线程可以访问共享资源。

生产者线程:生成数据后,发出 pthread_cond_signal 信号,通知等待的消费者。

当消费者被唤醒后,它重新获取互斥锁并处理共享资源。

pthread_cond_broadcast

是 POSIX 线程库中的一个函数,用于唤醒等待在指定条件变量上的所有线程。与 pthread_cond_signal 不同,pthread_cond_signal 只唤醒一个等待的线程,而 pthread_cond_broadcast 会唤醒所有等待线程。

int pthread_cond_broadcast(pthread_cond_t *cond);

cond:指向条件变量的指针(类型为 pthread_cond_t)。
返回值:如果成功返回 0,如果失败返回错误代码。

pthread_cond_broadcast 唤醒所有当前等待在 cond 条件变量上的线程。通常用于多个线程可能需要响应某个条件变化的场景。这些线程会尝试重新获得与条件变量关联的互斥锁,并在锁定成功后继续执行。

cp问题

生产者 VS 生产者 : 竞争关系,互斥关系

生产者 VS 消费者 :互斥关系,原子性。同步。

消费者 VS 消费者 : 互斥关系

3种关系,2种角色–生产者消费者,1个消费场所–特定结构的内存空间

优点:支持忙闲不均。生产与消费进行解耦。

伪唤醒

在生产消费者模型下,队列还剩余一个被生产满,刚好这时唤醒了一批线程,其中一个抢到了锁,并将队列生产满,释放锁之后,这个锁又刚好被上次唤醒的线程抢到,又要去执行生产任务,但是队列是满的,所以就发生了伪唤醒的情况。

信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。

//始化信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
//参数:
//pshared:0表示线程间共享,非零表示进程间共享
//value:信号量初始值//销毁信号量
int sem_destroy(sem_t *sem);//等待信号量
//功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem); //P()//发布信号量
//功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);//V()

基于环形队列的生产消费者模型:

在这里插入图片描述

多线程的互斥用信号量

信号量可以用于控制多个线程对共享资源的访问,尤其是在需要限制并发数量的场合。比如,使用计数信号量可以允许一定数量的线程同时访问资源,从而实现并发控制。

单线程的互斥用锁

锁(如互斥锁)适合于保护共享资源的独占访问,确保在任何时刻只有一个线程能够进入临界区。单线程的情况下,锁的使用更为简单和直接,因为不需要管理并发访问的复杂性。

  • 信号量适用于多线程环境下的资源计数和控制,并发访问。
  • 更适合于需要独占访问的情境,确保数据一致性。
    可以允许一定数量的线程同时访问资源,从而实现并发控制。

http://www.ppmy.cn/server/133599.html

相关文章

【网络】HTTP协议(2)

【网络】HTTP协议&#xff08;2&#xff09; 一. HTTP协议1.认识“方法”GET方法POST方法GET与POST的区别 2.常见状态码状态码小结 3.认识“Header”4.构造HTTP请求&#xff08;使用Postman&#xff09; 一. HTTP协议 1.认识“方法” GET方法 GET请求的特点&#xff1a;URL的…

界面控件Telerik UI for WPF 2024 Q3亮点 - 支持禁用数据过滤等

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序&#xff0c;同时还能快速构建企业级办公WPF应用程序。UI for WPF支持MVVM、触摸等&#xff0c;创建的应用程序可靠且结构良好&#xff0c;非常容易维护&#xff0c;其直观的API将无缝地集成Visual Studio…

opencv 按位操作

opencv位运算说明 按位与&#xff0c;按位或&#xff0c;按位非&#xff0c;按位异或 在 OpenCV 中&#xff0c;按位操作函数的接口一般包括两个或多个图像数组&#xff08;矩阵&#xff09;作为输入&#xff0c;常常还会有一个可选的掩码参数。下面我列出每个函数的具体接口…

【p2p、分布式,区块链笔记 Blockchain】truffle002 unleashed_rentable_nft 项目

上一篇&#xff1a;【p2p、分布式&#xff0c;区块链笔记 Blockchain】truffle001 以太坊开发框架truffle初步实践 项目结构 项目实现了一个简单的可租赁的 NFT 系统&#xff0c;用户可以铸造和销毁 NFT。这是作者写的项目介绍&#xff08;后边看issue才发现的&#xff09;&a…

Linux常用命令详细解析(含完整命令演示过程)

目录 1. 目录结构介绍 2. Linux命令基础 2.1 命令和命令行 2.2 格式 3. 常用命令 3.1 产看目录命令——ls 3.2 通配符 3.3 改变工作目录命令——cd 3.4 查看当前路径命令——pwd 3.5 创建新的目录命令——mkdir 3.6 创建文件目录命令——touch 3.7 查看…

Android 原生程序使用gdb, addr2line, readelf调试

Platform: RK3368 OS: Android 6.0 Kernel: 3.10.0 文章目录 一 gdb1. 原生程序添加调试符号2. 主机上adb push 编译好的原生程序到设备3. 设备上使用gdbserver运行原生程序4. 主机上设置adb端口转发5. 主机上运行gdb调试 二 addr2line三 readelf 一 gdb GDB&#xff08;GNU…

云计算-----单机LNMP结构WordPress网站

LNMP结构 博客网站 day1 小伙伴们&#xff0c;LNMP结构在第一二阶段浅浅的学习过&#xff0c;这里我们可以离线部署该结构。L指&#xff08;虚拟机&#xff09;服务器&#xff0c;nginx&#xff08;前端代理服务器&#xff09;mysql数据库&#xff0c;最后基于php建设动态…

Windows10去掉隐藏文件仍找不到hosts文件的解决办法

正常情况下hosts文件在目录C:\Windows\System32\drivers\etc中&#xff0c;最近新装的Windows10系统发现该目录下没有hosts文件。 执行如下命令hosts文件出现&#xff1a; 执行 for /f %P in (dir %windir%\WinSxS\hosts /b /s) do copy %P %windir%\System32\drivers\etc &am…