Linux多线程编程条件变量的概述和使用方法

news2024/11/17 22:16:21

目录

概述

1 引入条件变量概念

2 条件变量的应用

2.1 创建与销毁

2.1.1 创建条件变量

2.1.2 销毁条件变量

2.2 等待与通知

2.2.1 等待

2.2.2 通知

3 使用条件变量的范例

3.1 编写代码

3.2 测试

4 参考文献


概述

本文介绍了linux多线程编程中条件变量的相关知识,包括概念,函数体等内容,还编写一个实用的案例来介绍条件变量在实际编程中的使用方法。并在板卡中验证它的功能。

1 引入条件变量概念

在多线程编程中仅使用互斥锁来完成互斥是不够用的, 如以下情形:假设有两个线程 thread-1 和 thread-2, 需要这个两个线程循环对一个共享变量 sum 进行自增操作,那么 thread-1 和thread-2 只需要使用互斥量即可保证操作正确完成,线程执行代码如所示:

pthread_mutex_t sumlock= PTHREAD_MUTEX_INITIALIZER;
​
void * thread1(void) 
{
    pthread_mutex_lock(&sumlock);
    sum++;
    pthread_mutex_unlock(&sumlock);
}
​
void * thread2(void) 
{
    pthread_mutex_lock(&sumlock);
    sum++;
    pthread_mutex_unlock(&sumlock);
}

如果这时需要增加另一个线程 thread-3,需要 thread-3 在 count 大于 100 时将 count 值重新置 0 值, 那么可以 thread-3 可以实现如下:

void * thread3 (void) 
{
    pthread_mutex_lock(&sumlock);
    if (sum >= 100) {
        sum = 0;
        pthread_mutex_unlock(&sumlock);
    } 
    else {
        pthread_mutex_unlock(&sumlock);
        usleep(100);
    }
}

以上代码存在以下问题:

1) sum 在大多数情况下不会到达 100, 那么对 thread-3 的代码来说,大多数情况下执行 else分支, 只是 lock 和 unlock,然后进入 sleep(), 这浪费了 CPU 处理时间。

2) 为了节省 CPU 处理时间, thread-3 会在探测到 sum 没到达 100 的时候 usleep()一段时间。这样却又带来另外一个问题: 即 thread-3 响应速度下降。 可能在 sum 到达 200 的时候, thread-3 才会醒过来。

怎么解决这个问题呢?

这样时间与效率出现了矛盾,而条件变量就是解决这个问题的好方法。

2 条件变量的应用

2.1 创建与销毁

2.1.1 创建条件变量

pthreads 用 pthread_cond_t 类型的变量来表示条件变量。程序必须在使用 pthread_cond_t 变量之前对其进行初始化。

(1) 静态初始化

对于静态分配的变量可以简单地将 PTHREAD_COND_INITIALIZER 赋值给变量来初始化默认行为的条件变量。

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

(2)动态初始化

对动态分配或者不使用默认属性的条件变量来说可以使用 pthread _cond_init()来初始化。函数原型如下:

int pthread_cond_init( pthread_cond_t *restrict cond,
                       const pthread_condattr_t *restrict attr);

参数介绍:

参数描述
cond参数 cond 是一个指向需要初始化 pthread_cond_t 变量的指针
attr参数 attr 传递 NULL 值时,pthread_cond_init()将 cond 初始化为默认属性的条件变量。函数成功将返回 0;否则返回一个非 0 的错误码。

静态初始化程序通常比调用 pthread_cond_init()更有效,而且在任何线程开始执行之前,确保变量被执行一次。

一个条件变量的初始化的案例 :

pthread_cond_t cond;
int error;
​
if (error = pthread_cond_init(&cond, NULL)){
   fprintf(stderr, "Failed to initialize cond : %s\n", strerror(error));
 }

2.1.2 销毁条件变量

函数 pthread_cond_destroy()用来销毁它参数所指出的条件变量,函数原型如下:

int pthread_cond_destroy(pthread_cond_t *cond);

函数成功调用返回 0,否则返回一个非 0 的错误码。以下代码演示了如何销毁一个条件变量。

pthread_cond_t cond;
int error;
​
if (error = pthread_cond_destroy(&cond)){
     fprintf(stderr, "Failed to destroy cond : %s\n", strerror(error));
 }

2.2 等待与通知

2.2.1 等待

条件变量是与条件测试一起使用的,通常线程会对一个条件进行测试,如果条件不满足就会调用条件等待函数来等待条件满足。

条件等待函数如下:

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);

功能介绍

1)pthread_cond_wait()函数: 在条件不满足时将一直等待

2 pthread_cond_timedwait() 函数: 等待一段时间。

pthread_cond_timedwait() 函数参数介绍:

参数描述
cond指向条件变量的指针
mutex指向互斥量的指针,线程在调用前应该拥有这个互斥量,当线程要加入条件变量的等待队列时,等待操作会使线程释放这个互斥量
abstime返回时间的指针,如果条件变量通知信号没有在此等待时间之前出现,等待将超时退出, abstime 是个绝对时间,而不是时间间隔

函数返回值:

成功调用返回 0,否则返回非 0 的错误码,其中 pthread_cond_timedwait() 函数如果 abstime 指定的时间到期,错误码为 ETIMEOUT。

一个实例:

void function()
{
    pthread_mutex_lock(&mutex)
    
    while(a < b){
        pthread_cond_wait(&cond, &mutex)
    }
    
    pthread_mutex_unlock(&mutex)
}

2.2.2 通知

当另一个线程修改了某参数可能使得条件变量所关联的条件变成真时,它应该通知一个或者多个等待在条件变量等待队列中的线程。

函数名功能介绍
pthread_cond_signal()可以唤醒一个在条件变量等待队列等待的线程
pthread_cond_broadcast()可以所有在条件变量等待队列等待的线程

函数原型如下:

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

参数 cond 是一个指向条件变量的指针。函数成功返回 0,否则返回一个非 0 的错误码。

3 使用条件变量的范例

3.1 编写代码

创建 test_thread.c文件,编写如下代码:

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     :  test_thread.c
作者       : tangmingfei2013@126.com
版本       : V1.0
描述       : pthread API test
其他       : 无
日志       : 初版V1.0 2024/03/04
​
***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
​
static pthread_t tid[3];
static int sum = 0;
static pthread_mutex_t sumlock = PTHREAD_MUTEX_INITIALIZER;        /* 静态初始化互斥量 */
static pthread_cond_t cond_sum_ready = PTHREAD_COND_INITIALIZER;   /* 静态初始化条件变量 */
​
void * thread_12(void *arg) 
{
    int i;
    long id = (long)arg;
​
    for (i = 0; i < 60; i++) {
        pthread_mutex_lock(&sumlock);             /* 使用互斥量保护临界变量 */
        sum++;
        printf("thread %ld: read sum value = %d\n", id + 1 , sum);
        pthread_mutex_unlock(&sumlock);
        
        if (sum >= 100){
            pthread_cond_signal(&cond_sum_ready); /* 发送条件通知,唤醒等待线程 */
        }
    }
    
    return NULL;
}
​
void * thread_3(void *arg) 
{
    pthread_mutex_lock(&sumlock);
    
    while(sum < 100){                                 /* 不满足条件将一直等待 */
        pthread_cond_wait(&cond_sum_ready, &sumlock); /* 等待条件满足 */
    }
    sum = 0;
    printf("t3: clear sum value\n");
    
    pthread_mutex_unlock(&sumlock);
    
    return NULL;
}
​
int main(void) 
{
    int err;
    long i;
    
    for (i = 0; i < 2; i++) 
    {
         /* 创建线程 1 线程 2 */
        err = pthread_create(&(tid[i]), NULL, &thread_12, (void *)i); 
        if (err != 0) {
            printf("Can't create thread :[%s]", strerror(err));
        } 
    }
    
    err = pthread_create(&(tid[2]), NULL, &thread_3, NULL);    /* 创建线程 3 */
    if (err != 0)
        printf("Can't create thread :[%s]", strerror(err));
    
    for (i = 0; i < 3; i++){
        pthread_join(tid[i], NULL);
    }
    
    return 0;
}
​

3.2 测试

编译代码,然后在板卡上运行:

1)线程2首先得到互斥锁,count++,一直加到60

2)线程1得到互斥锁,count++,一直加到100 , 发出条件通知

2)线程3得到互斥锁,收到条件通知,然后清除计数,解锁互斥量

4 参考文献

  1. 《现代操作系统》

  2. 《linux/unix系统编程手册》

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

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

相关文章

09-设计模式 面试题

你之前项目中用过设计模式吗? 工厂方法模式分类 简单工厂模式工厂方法模式抽象工厂模式工厂模式 需求:设计一个咖啡店点餐系统。 设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore)…

qsort函数的模拟和使用(两万字详解)

qsort 的使用&#xff08;回调函数结构体指针的总和运用&#xff09; qsort的作用 qsort--用来排序的 库函数&#xff0c;直接可是用来排序数据 底层使用的是快速排序的方式 —————————————————————————————————————————————…

【绿电监测 碳排放分析 新建5G基站】基站能效管理解决方案

背景及需求 中国基站相关政策 需求 01用电监管&#xff0c;偷电窃电监测 对基站进线回路和出线回路进行监测对比&#xff0c;实时监测线路的使用功率&#xff0c;通过最大功率判断是否有其他设备接入而产生偷电行为。 02节能控制 通过控制空调启停、调整通讯设备工作模式或…

http升级https需要做什么

背景&#xff1a;随着现代网络时代的高速发展&#xff0c;网络安全方面的日益更新&#xff0c;实现网站https协议的数量也在不断增多&#xff0c;完善安全方面的因素也在逐步增加。 下面从最基础的网站http协议全面升级为https协议的流程做出说明。 目录 首先带大家一起先了解…

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 4-3、线条平滑曲面(原始颜色)去除无效点

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fr…

HTML5七天学会基础动画网页10(2)

制作立方体 学完前面的基础内容&#xff0c;制作立方体是个不错的练习方法&#xff0c;先看成品 再分析一下&#xff0c;六个面让每个面旋转平移就可以实现一个立方体&#xff0c;来看代码: <title> 制作立方体</title> <style> *{ margin: 0; padding: 0; …

Springboot+vue的政府管理的系统设计(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的政府管理的系统设计&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff…

案例分析篇01:软件架构设计考点架构风格及质量属性(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12601310.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

【大厂AI课学习笔记NO.79】机器学习行业人才能力图谱

有从事机器学习行业的小伙伴&#xff0c;人才岗位如上了。 同样的&#xff0c;也是分为领军人才&#xff08;略&#xff09;、产业研发人才、应用开发人才和实用技能人才了。 机器学习领域的就业岗位分析 随着科技的飞速发展&#xff0c;人工智能已成为当今时代最热门的领域…

Purple Pi OH鸿蒙开发板7天入门OpenHarmony开源鸿蒙教程【五】

在完成了Purple Pi OH大部分的接口测试之后&#xff0c;紧接着就是一个充满挑战的任务——利用SDK来编译生成我们自己的镜像文件。通过这一过程&#xff0c;不仅能够让你获得一个可在真实硬件上运行的系统镜像&#xff0c;更重要的是&#xff0c;它让你对OpenHarmony系统的构建…

分享个好用的GPT网站

目录 一、背景 二、功能描述 1、写代码 2、联网查询 3、AI绘图 一、背景 我现在的开发工作都依靠ChatGPT&#xff0c;效率提升了好几倍。这样一来&#xff0c;我有更多时间来摸鱼&#xff0c;真是嘎嘎香~ ⭐⭐⭐点击直达 ⭐⭐⭐ 二、功能描述 1、写代码 import java.ut…

Hyperopt自动化调参工具实践-1

hyperopt Hyperopt的任务是在一组可能的参数上找到标量值的最佳值&#xff0c;该标量值可能是随机的。 与许多优化包假定这些输入来自向量空间不同&#xff0c;Hyperopt是不同的&#xff0c;因为它鼓励使用者更详细地描述搜索空间。通过提供关于函数定义在哪里以及认为最佳值…

AI新工具(20240311) 国内免费使用Claude 3 Sonnet;Pika推出视频加音效功能

1: 国内免费使用Claude 3 Sonnet Claude 3现已登陆Amazon Bedrock&#xff0c;国内就能够免费使用&#xff0c;以下是网友整理的使用流程。 地址&#xff1a;https://lab.amazoncloud.cn/ 2: Pika Sound Effects Pika推出视频加音效功能&#xff0c;为视频创作带来声音定制…

举牌小人图生成小程序源码(修复版)

源码介绍&#xff1a; 举牌小人图生成小程序源码&#xff08;修复版&#xff09;无需服务器导入开发者工具即可运行&#xff0c;无需绑定合法域名&#xff0c;仅供学习交流 建议&#xff1a; 有能力者接入安全过滤机制&#xff0c;更完美&#xff0c;可以联系客服免费指导~ 源…

深度学习_VGG_3

目标 知道VGG网络结构的特点能够利用VGG完成图像分类 2014年&#xff0c;牛津大学计算机视觉组&#xff08;Visual Geometry Group&#xff09;和Google DeepMind公司的研究员一起研发出了新的深度卷积神经网络&#xff1a;VGGNet&#xff0c;并取得了ILSVRC2014比赛分类项目…

OKHttpRetrofit

完成一个get请求 1.导入依赖 implementation("com.squareup.okhttp3:okhttp:3.14.")2.开启viewBinding android.buildFeatures.viewBinding true 3.加网络权限 和 http明文请求允许配置文件 <?xml version"1.0" encoding"utf-8"?> &l…

【精选】30+Redis面试题整理(2024)附答案

目录 前言Redis基础项目有用到redis吗&#xff1f;你们项目为什么用redis?redis为什么这么快&#xff1f;了解Redis的线程模型吗&#xff1f;Redis优缺点?redis如何实现持久化&#xff1f;RDB持久化过程&#xff1f;AOF持久化过程&#xff1f;AOF持久化会出现阻塞吗&#xff…

[Angular 基础] - 表单:响应式表单

[Angular 基础] - 表单&#xff1a;响应式表单 之前的笔记&#xff1a; [Angular 基础] - routing 路由(下) [Angular 基础] - Observable [Angular 基础] - 表单&#xff1a;模板驱动表单 开始 其实这里的表单和之前 Template-Driven Forms 没差很多&#xff0c;不过 Tem…

vue-创建vue项目记录

安装node.js 先安装node.js的运行环境node.js的下载地址 安装后就可以使用npm命令 1、清除npm缓存&#xff1a;npm cache clean --force 2、禁用SSL&#xff1a;npm config set strict-ssl false 3、手动设置npm镜像源&#xff1a;npm config set registry https://registry.…

Python AI 之Stable-Diffusion-WebUI

Stable-Diffusion-WebUI简介 通过Gradio库&#xff0c;实现Stable Diffusion web 管理接口 Windows 11 安装Stable-Diffusion-WebUI 个人认为Stable-Diffusion-WebUI 官网提供的代码安装手册/自动安装不适合新手安装&#xff0c;我这边将一步步讲述我是如何搭建Python Conda…