[Linux]条件变量:实现线程同步(什么是条件变量、为什么需要条件变量,怎么使用条件变量(接口)、例子,代码演示(生产者消费者模式))

news2024/9/21 12:43:57

目录

一、条件变量

1.什么是条件变量

故事说明

2、为什么需要使用条件变量

竞态条件

 3.什么是同步 

饥饿问题

二、条件变量的接口

1.pthread_cond_t

2.初始化(pthread_cond_init)

3.销毁(pthread_cond_destroy)

4.等待(pthread_cond_wait)

5.唤醒(pthread_cond_signal && pthread_cond_broadcast)

pthread_cond_signal

pthread_cond_broadcast

三、使用演示 (模拟生产者消费者模式)


一、条件变量

1.什么是条件变量

条件变量(Condition Variable)是一种用于线程同步的机制,通常与互斥锁(Mutex)一起使用。条件变量提供了一种线程间的通信机制,允许一个线程等待另一个线程满足某个条件后再继续执行。

故事说明

现在小明要在在一张桌子上放一个苹果,而旁边有一群蒙着眼睛的人,因为他们的眼睛被蒙着,他们如果想拿到这个苹果,就会时不时来桌子前摸一摸看看桌子是否有苹果,并且谁来桌子前摸苹果是无序的,这时的场面就很混乱,小明一看不行,于是小明就桌子上放了个铃铛,并且组织需要苹果的人排好队,有苹果小米就会摇响铃铛,排在第一个的人就拿走苹果,排到队尾等待被唤醒。此时混乱的场面就显得尽然有序了。在本故事中,小明就是操作系统,苹果就是临界资源,铃铛就是条件变量,排队就是实现同步,摇响铃铛就是唤醒线程。

2、为什么需要使用条件变量

使用条件变量主要是因为它们提供了在多线程编程中一种有效的同步机制。当多个线程需要等待某个特定条件成立才能继续执行时,条件变量就显得尤为重要。通过条件变量,线程可以安全地进入等待状态,直到被其他线程显式地唤醒或满足等待的条件。这有助于避免线程的无谓轮询或忙等待,提高了系统的响应能力和效率。

注意:在使用条件变量时,必须确保与互斥锁一起使用,以避免竞态条件的发生

竞态条件

竞态条件(Race Condition)是指在设备或系统尝试同时执行两个或多个操作时,由于操作顺序不当而导致的不期望的结果。简单来说就是因为时序问题,而导致程序异常。
 

 3.什么是同步 

在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步。

饥饿问题

饥饿问题指的是某些线程由于某种原因无法获得它们所需要的资源或执行机会,导致它们长时间得不到处理,甚至永远得不到处理的现象。这种情况通常发生在多个线程竞争有限资源时,其中一些线程可能因为优先级过低、调度算法的不公平性、同步机制使用不当或其他原因而无法获得足够的执行时间。

二、条件变量的接口

1.pthread_cond_t

pthread_cond_t 是 POSIX 线程库(Pthreads)中用于表示条件变量的数据类型。

2.初始化(pthread_cond_init)

功能:初始化条件变量
原型

#include <pthread.h>

方式一(pthread_cond_t是局部全局都可以用):

int pthread_cond_init(pthread_cond_t *restrict cond,

                       const pthread_condattr_t *restrict attr);

方式二(pthread_cond_t是全局变量时):
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

注意:restrict 是一个类型限定符,它用于告知编译器两个指针不会指向同一个内存位置,这样编译器可以生成更高效的代码

参数

  • cond:一个指向 pthread_cond_t 类型的指针,用于存储初始化后的条件变量。
  • attr:一个指向 pthread_condattr_t 类型的指针,用于指定条件变量的属性。通常可以传递 NULL(nullptr),以使用默认属性。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

使用例子:

#include <pthread.h>  
#include <stdio.h>  
  
pthread_cond_t cond; // 全局 pthread_cond_t 变量  
  
int main() {  
    int rc;  
  
    // 显式初始化全局 pthread_cond_t 变量  
    rc = pthread_cond_init(&cond, NULL);  
    if (rc != 0) {  
        printf("Cond init failed: %d\n", rc);  
        return 1;  
    }  
  
    // ... 其他代码,包括线程创建和同步 ...  
  
    // 在不再需要条件变量时销毁它  
    //...
  
    return 0;  
}

3.销毁(pthread_cond_destroy)

功能:销毁条件变量
原型

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);

参数

  • cond:指向要销毁的条件变量的指针。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

4.等待(pthread_cond_wait)

功能:阻塞当前线程,直到指定的条件变量被其他线程信号通知或广播。
原型

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *restrict cond,
                  pthread_mutex_t *restrict mutex);

参数

  • cond:指向条件变量的指针。
  • mutex:指向互斥锁的指针,该互斥锁应该在调用 pthread_cond_wait 之前由当前线程锁定。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

5.唤醒(pthread_cond_signal && pthread_cond_broadcast)

pthread_cond_signal

功能:唤醒正在等待特定条件变量的一个线程
原型

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);

参数

  • cond:指向要发送信号(广播)的条件变量的指针。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

pthread_cond_broadcast

功能:用于唤醒所有正在等待指定条件变量的线程
原型

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);

参数

  • cond:指向要发送信号(广播)的条件变量的指针。

返回值

  • 如果成功,返回 0。
  • 如果失败,返回错误码。

三、使用演示 (模拟生产者消费者模式)

说明:模拟生产者消费者模式

注意:使用pthrad原生线程库(POSIX库)要链接库:-lpthread

不会连接动态库的可以看我这篇文章:[Linux]动静态库(什么是动静态库,怎么生成动静态库,怎么使用(连接)动静态库)-CSDN博客

cond.cc

#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <unistd.h>
using namespace std;

// 定义条件变量和互斥锁
// 全局的初始化方式
pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 共享变量,用于线程间的同步
int shared_data = 0;

// 线程函数,模拟生产者
void *producer(void *args)
{
    string producer_name = static_cast<char *>(args);
    // 生产数据,并通知消费者
    while (1)
    {
        // 锁定互斥锁
        pthread_mutex_lock(&mutex);

        // 生产数据(这里只是简单地递增shared_data)
        shared_data++;
        cout << "I is " << producer_name << " "
             << " Producer produced data: "
             << shared_data << endl;

        // 唤醒等待的消费者线程
        pthread_cond_signal(&cond_var);

        // 解锁互斥锁
        pthread_mutex_unlock(&mutex);

        // 模拟生产耗时
        sleep(1);
    }

    return nullptr;
}

// 线程函数,模拟消费者
void *consumer(void *args)
{

    string consumer_name = static_cast<char *>(args);
    // 消费数据
    while (1)
    {
        // 锁定互斥锁
        pthread_mutex_lock(&mutex);

        // 等待生产者生产数据
        while (shared_data == 0)
        {
            // 等待条件变量,解锁互斥锁,进入等待状态
            pthread_cond_wait(&cond_var, &mutex);
        }

        // 消费数据(这里只是简单地递减shared_data)
        shared_data--;
        cout << "I is " << consumer_name << " "
             << " Consumer consumed data: "
             << shared_data << endl;
        cout << "-----------------------------------"
             << endl;

        // 解锁互斥锁
        pthread_mutex_unlock(&mutex);

        // 模拟消费耗时
        sleep(4);
    }

    return nullptr;
}

int main()
{

    int producer_thread_num = 5; // 生产者人数
    int consumer_thread_num = 10; // 消费者人数
    vector<pthread_t> producers;
    vector<pthread_t> consumers;

    for (int i = 0; i < producer_thread_num; i++)
    {
        pthread_t producer_thread; // 生产者
        char buffer[64];
        sprintf(buffer, "producer-%d", i + 1);
        // 创建生产者线程
        if (pthread_create(&producer_thread, nullptr, producer, buffer) != 0)
        {
            perror("pthread_create producer");
            exit(EXIT_FAILURE);
        }
        producers.push_back(producer_thread);//保存pthread_t,以备等待回收
    }

    for (int i = 0; i < consumer_thread_num; i++)
    {
        pthread_t consumer_thread; // 消费者
        // 创建消费者线程
        char buffer[64];
        sprintf(buffer, "consumer-%d", i + 1);
        if (pthread_create(&consumer_thread, nullptr, consumer, buffer) != 0)
        {
            perror("pthread_create consumer");
            exit(EXIT_FAILURE);
        }
        consumers.push_back(consumer_thread);//保存pthread_t,以备等待回收
    }

    // 等待线程结束
    for (auto& thraed:producers)
    {   
        pthread_join(thraed, nullptr);
    }
    for (auto& thraed:consumers)
    {   
        pthread_join(thraed, nullptr);
    }

    // 销毁条件变量
    pthread_cond_destroy(&cond_var);
    // 销毁锁
    pthread_mutex_destroy(&mutex);

    return 0;
}

Makefile

mycond:cond.cc
	g++ -o $@ $^ -std=c++11 -lpthread
PHONY:clean
clean:
	rm -f mycond

结果

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

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

相关文章

OKR与敏捷开发、精益创业等方法如何协同工作?

在快速变化的市场环境中&#xff0c;企业需要更加灵活和高效地应对各种挑战。目标与关键成果法&#xff08;OKR&#xff09;、敏捷开发以及精益创业等方法&#xff0c;作为现代企业管理的重要工具&#xff0c;各自在推动企业发展、提高团队效率、优化产品迭代等方面发挥着不可或…

Excel 使用SQL统计表格数据

一. 需求 ⏹有如下Excel表格&#xff0c;现要求统计每个店铺的每种类别的商品总销量和最大销量 ⏹详细数据如下 店铺商品类别销量一山店苹果水果27729一山店梨水果76175一山店菠萝水果14699一山店香蕉水果61371一山店西兰花蔬菜72822一山店大白菜蔬菜65090一山店小白菜蔬菜13…

LLM - 大语言模型的分布式训练 概述

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/136924304 大语言模型的分布式训练是一个复杂的过程&#xff0c;涉及到将大规模的计算任务分散到多个计算节点上。这样做的目的是为了处…

部署Prometheus+grafana详解

目录 一、prometheus 介绍 二、prometheus 对比 zabbix 三、prometheus 监控插件 四、部署 1、下载所需的包 2.编辑prometheus的配置文件 3、编辑alertmanager 的配置文件 4、tmpl 模板&#xff08;将此文件创建在/opt/alertmanager/tmpl/&#xff09; 5.启动&#xff0…

【漏洞复现】Arris 路由器 basic_sett 信息泄露漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

c++编写菱形图和计算100~200之间的素数

c编写菱形图 #include <stdio.h> int main() {int i,j,k,n;printf("请输入n:\n");scanf("%d",&n);for(i1;i<n;i){for(k1;k<n-i;k)printf(" ");for(j1;j<2*i-1;j)printf("*");printf("\n");}for(i1;i<…

计算机二级(python)【一】

真题1 1、考生文件夹下存在一个文件PY101. py&#xff0c;请写代码替换横线&#xff0c;不修改其他代码&#xff0c;实现以下功能: 键盘输入正整数n&#xff0c;按要求把n输出到屏幕&#xff0c;格式要求:宽度为20个字符&#xff0c;减号字符-填充&#xff0c;右对齐&#xf…

Sora底层技术原理:Stable Diffusion运行原理

AIGC 热潮正猛烈地席卷开来&#xff0c;可以说 Stable Diffusion 开源发布把 AI 图像生成提高了全新高度&#xff0c;特别是 ControlNet 和 T2I-Adapter 控制模块的提出进一步提高生成可控性&#xff0c;也在逐渐改变一部分行业的生产模式。惊艳其出色表现&#xff0c;也不禁好…

MD5源码(C语言描述)

本文介绍MD5源码&#xff08;C语言描述&#xff09;。 MD5(Message-Digest Algorithm 5)&#xff0c;即消息摘要算法5&#xff0c;是一种被广泛使用的消息散列算法。散列算法的基础原理是&#xff1a;将数据&#xff08;如一段文字&#xff09;经过运算转换为一段固定长度&…

使用CUDA 为Tegra构建OpenCV

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;MultiArch与Ubuntu/Debian 的交叉编译 下一篇&#xff1a;在iOS中安装 警告&#xff1a; 本教程可能包含过时的信息。 使用CUDA for Tegra 的OpenCV 本文档是构建支持 CUD…

UE5制作一条底部挂着物体的悬垂的绳子

主要涉及cable&#xff08;缆索&#xff09;组件、PhysicsConstraint&#xff08;物理约束&#xff09;组件的灵活运用&#xff0c;经过摸索&#xff0c;写下本文以供探讨。 一、关卡中制作 关卡中制作最简单 1. cable组件加入场景 打开放置Actor面板&#xff0c;在其中找到…

docker swarm 集群创建

1&#xff0c;目的&#xff1a; 通过docker swarm 工具将一台或者多台安装了docker的服务器组成一个完整的集群&#xff0c;该集群中的node节点可以通过Leader节点管理。在使用docker stack部署时&#xff0c;可以将容器自动分发到合适的节点上。 2&#xff0c;服务器准备&am…

探秘开源隐语:架构深度剖析与隐私计算技术之旅

1.隐语架构 隐语&#xff08;SecretFlow&#xff09;作为蚂蚁集团开源的可信隐私计算框架&#xff0c;其架构设计具有多层次的特点&#xff0c;虽然具体分层名称可能会根据实际描述略有差异&#xff0c;但我们可以依据已有的技术和信息对其进行结构化的拆解&#xff1a; 硬件层…

GraalVM详细安装及打包springboot、java、javafx使用教程(环境安装篇)

下一篇:GraalVM详细安装及打包springboot、java、javafx使用教程(打包普通JAVA项目篇) GraalVM介绍 GraalVM是一款由Oracle公司开发的一款具有高效性能、降低基础设施成本、支持Java发展、与其他编程语言无缝集成、创建本机镜像等优点的跨平台虚拟机。它支持多种编程语言&…

Multi-Raft 架构, 数据Shard分区,数据迁移

Raft 与 Multi Raft PingCAP TiKV课程笔记课程链接 数据是以region&#xff08;也叫Raft Group)为单位进行存储的。一个region默认会有3个副本&#xff0c;存在不同的TiKV Node上。副本中的一个节点为leader。所有的读写流量只走leader&#xff0c;leader定期向follower发送心…

谷歌应用上架,如何选择IP?

在讨论IP对于谷歌上架的重要性或影响时&#xff0c;需要明确一点&#xff1a;开发者账号质量可以直接影响上架成功率&#xff0c;而IP是影响账号质量的重要因素之一。因此&#xff0c;IP对于谷歌上架的重要性&#xff0c;不言而喻。 我们都清楚&#xff0c;谷歌是不允许一个用户…

vue+element 前端实现增删查改+分页,不调用后端

前端实现增删查改分页&#xff0c;不调用后端。 大概就是对数组内的数据进行增删查改分页 没调什么样式&#xff0c;不想写后端&#xff0c;当做练习 <template><div><!-- 查询 --><el-form :inline"true" :model"formQuery">&l…

PHP的IntlChar类:处理Unicode字符的强大工具

PHP的IntlChar类&#xff1a;处理Unicode字符的强大工具 在处理多语言和国际化应用程序时&#xff0c;Unicode字符的解码是必不可少的一环。PHP的IntlChar类为我们提供了强大的工具来解码Unicode字符。本文将深入探讨PHP的IntlChar类&#xff0c;介绍其功能、用法和优势&#x…

鸿蒙:@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

在实际应用开发中&#xff0c;应用会根据开发需要&#xff0c;封装自己的数据模型。对于多层嵌套的情况&#xff0c;比如二维数组&#xff0c;或者数组项class&#xff0c;或者class的属性是class&#xff0c;他们的第二层的属性变化是无法观察到的。这就引出了Observed/Object…

基于python+vue的幼儿园管理系统flask-django-php-nodejs

随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建幼儿园管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种活动信息、课程信息、菜谱信息、通知公告、家…