当前位置: 首页 > news >正文

线程的同步

文章目录

  • 线程的同步
    • 同步:
    • 条件变量:
      • `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.mrgr.cn/news/54936.html

相关文章:

  • 3.matplotlib基础及用法(全)
  • C#中Task.ContinueWith如何使用
  • Flink窗口分配器WindowAssigner
  • 新一代Linux防火墙已经来临(iptables面临淘汰)
  • Python 高级编程详解
  • linux系统中chmod用法详解
  • 二、Linux 入门教程:开启大数据领域的神奇之旅
  • 【Linux】从多线程同步到生产者消费者模型:多线程编程实践
  • Qml-Item的Id生效范围
  • Java集合剖析2】Java集合底层常用数据结构
  • 利士策分享,财富多少,才是恰到好处?
  • 推荐一款多功能理科计算器:Math Resource Studio Pro
  • WPF入门_03路由事件
  • 数据结构(C语言):顺序表
  • WPF 回到主线程
  • Egg.js 项目的合理 ESLint 配置文件模板
  • 锁的原理以及使用
  • 《知道做到》
  • 【MySQL核心面试题】MySQL 核心 - Explain 执行计划详解!
  • 如何用AI大模型提升挖洞速度
  • upload-labs Pass-04
  • 使用 NASM 和 Windows API 创建一个简单窗口的完整实例
  • 图幅结合表DWG转DXF,使用DXF文件进行批量影像分幅
  • 字面量优化、alignas和alignof、属性说明符和标准属性
  • Java方法的递归调用
  • 27.2 动态分片方案和它要解决的问题