首页 > 系统相关 >Linux线程同步(条件变量)

Linux线程同步(条件变量)

时间:2023-08-09 20:33:33浏览次数:46  
标签:同步 变量 buffer Linux cond pthread 条件 线程

(文章目录)


前言

本篇文章来讲解一下条件变量的使用。

一、条件变量概念

条件变量(Condition Variable)是并发编程中一种线程同步机制,用于实现线程之间的等待和通知机制。它是一种与特定条件相关的线程同步原语。

条件变量用于线程间的协调,允许一个线程在满足某个特定条件之前等待,并在其他线程满足条件后被通知继续执行。它通常与互斥锁(Mutex)结合使用,以提供更精细的线程同步和共享数据的访问控制。

条件变量的基本概念包括以下几个要素:

1.等待和通知: 条件变量提供了等待和通知的机制,等待(Wait)操作用于使线程进入等待状态,直到满足某个特定条件。通知(Signal或Broadcast)操作用于唤醒等待中的线程,告知它们条件已经满足。

2.互斥锁: 在使用条件变量之前,通常需要先获取与之配套的互斥锁,以确保共享数据的互斥访问。互斥锁用于保护与条件相关的共享资源,以防止多个线程同时访问。

3.条件谓词: 条件变量通常与条件谓词(Condition Predicate)一起使用,条件谓词是描述条件是否满足的谓词表达式。在线程等待之前和通知之后,都需要通过条件谓词进行条件的检查。

条件变量的典型用法包括以下步骤:

1.线程获取互斥锁,锁定共享资源。

2.检查条件谓词,判断是否满足等待条件,如果不满足则等待条件变量。

3.线程释放互斥锁并进入等待状态,直到其他线程发出通知。

4.当其他线程满足条件变量的条件并发出通知时,等待中的线程被唤醒。

5.线程重新获取互斥锁,继续执行后续操作。

条件变量的使用可以避免线程的忙等待,提高程序的效率,并且更加灵活地实现线程间的通信和同步。但在使用条件变量时需要注意正确的加锁和解锁的顺序,以避免死锁和竞态条件等并发编程的常见问题。

二、条件变量相关的函数

条件变量在Linux下使用一些常见的函数来实现线程之间的同步和通信。下面是条件变量使用的几个常用函数:

1.pthread_cond_init():用于初始化条件变量。‘

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

参数 cond:指向要初始化的条件变量。 参数 attr:指向条件变量的属性对象,通常传递 NULL。 返回值:成功返回0,失败返回错误码。

2.pthread_cond_destroy():用于销毁条件变量。

int pthread_cond_destroy(pthread_cond_t *cond);

参数 cond:指向要销毁的条件变量。 返回值:成功返回0,失败返回错误码。

3.pthread_cond_wait():用于等待条件变量满足条件。

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

参数 cond:指向要等待的条件变量。 参数 mutex:指向与条件变量关联的互斥锁。 返回值:成功返回0,失败返回错误码。 在调用 pthread_cond_wait() 之前,必须先获得与条件变量关联的互斥锁 mutex 的锁,然后该函数会自动释放 mutex 的锁,并让线程进入等待状态,直到被另一个线程通过 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒。

4.pthread_cond_signal():用于唤醒一个正在等待条件变量的线程。

int pthread_cond_signal(pthread_cond_t *cond);

参数 cond:指向要唤醒的条件变量。 返回值:成功返回0,失败返回错误码。 pthread_cond_signal() 会唤醒等待 cond 条件变量的一个线程,如果没有线程在等待条件变量,则该函数调用没有任何效果。

5.pthread_cond_broadcast():用于唤醒所有正在等待条件变量的线程。

int pthread_cond_broadcast(pthread_cond_t *cond);

参数 cond:指向要广播的条件变量。 返回值:成功返回0,失败返回错误码。 pthread_cond_broadcast() 会唤醒等待 cond 条件变量的所有线程,即广播唤醒所有等待的线程。

三、条件变量模拟生产者消费者模型

示例代码:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

#define BUFFER_SIZE 10

typedef struct {
    int buffer[BUFFER_SIZE];
    int count;
    int in;
    int out;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} Buffer;

Buffer buffer;

void* producer(void* arg) 
{
    int item = 1;
    while (1) 
    {
        pthread_mutex_lock(&buffer.mutex);
        
        // 检查缓冲区是否已满
        while (buffer.count == BUFFER_SIZE) 
        {
            pthread_cond_wait(&buffer.cond, &buffer.mutex);
        }
        
        // 写入数据并更新缓冲区状态
        buffer.buffer[buffer.in] = item;
        buffer.in = (buffer.in + 1) % BUFFER_SIZE;
        buffer.count++;
        printf("生产者生产了:%d\n", item);

        // 唤醒等待的消费者线程
        pthread_cond_signal(&buffer.cond);
        
        pthread_mutex_unlock(&buffer.mutex);
        
        item++;
        sleep(1);
    }
    return NULL;
}

void* consumer(void* arg) 
{
    while (1) 
    {
        pthread_mutex_lock(&buffer.mutex);
        
        // 检查缓冲区是否为空
        while (buffer.count == 0) 
        {
            pthread_cond_wait(&buffer.cond, &buffer.mutex);
        }
        
        // 读取数据并更新缓冲区状态
        int item = buffer.buffer[buffer.out];
        buffer.out = (buffer.out + 1) % BUFFER_SIZE;
        buffer.count--;
        printf("消费者消费了:%d\n", item);
        
        // 唤醒等待的生产者线程
        pthread_cond_signal(&buffer.cond);
        
        pthread_mutex_unlock(&buffer.mutex);
        
        sleep(1);
    }
    return NULL;
}

int main(void)
{

    pthread_t producer_thread, consumer_thread;
    
    // 初始化缓冲区和相关的同步对象
    buffer.count = 0;
    buffer.in = 0;
    buffer.out = 0;
    pthread_mutex_init(&buffer.mutex, NULL);
    pthread_cond_init(&buffer.cond, NULL);

    // 创建生产者线程和消费者线程
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);
    
    // 等待线程结束
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);
    
    // 销毁同步对象
    pthread_mutex_destroy(&buffer.mutex);
    pthread_cond_destroy(&buffer.cond);

    

    return 0;
}

运行效果: 在这里插入图片描述

四、使用条件变量的好处

1.避免资源浪费:当条件不满足时,线程可以调用pthread_cond_wait函数来等待条件满足。在等待期间,该线程会释放持有的锁,允许其他线程获得锁并继续执行临界区代码。这样可以避免空转和资源浪费。

2.避免竞争条件:条件变量的使用可以帮助避免竞争条件的发生。线程间的协作通过条件变量来实现,确保在满足特定条件之前,线程不会执行关键代码段。

3.提高并发性:条件变量使得线程能够在需要等待条件满足时休眠,而不是通过忙等待消耗处理器资源。这样可以提高系统的并发性和效率。

4.简化同步逻辑:使用条件变量可以简化同步逻辑,使代码更加清晰易懂。条件变量提供了一种灵活而有效的机制来控制线程的行为,使得编写正确的多线程代码变得更加容易。

总结

本篇文章主要讲解了条件变量的概念和条件变量的相关函数和使用条件变量模拟生产者消费者模型。

标签:同步,变量,buffer,Linux,cond,pthread,条件,线程
From: https://blog.51cto.com/u_16153875/7024890

相关文章

  • 开放式字幕——声画同步效果
    把窗口调到字母和图形也可以窗口-文本在下面调整字母时间想改样式就在基本图形里面......
  • Linux系统简介
    程序员必备的技能:一门编程语言:C语言、C++数据结构与算法:表、树、图、查找、排序、STL操作系统:Linux操作系统网络通信:TCP\IP协议簇(Socket套接字技术、TCP、UDP、FTP、HTTP等协议)数据库:MySQL界面设计:Qt操作系统课程内容:系统介绍、内存管理、文件管理、信号处理、进程管理......
  • linux安装nodejs
    安装node使用node安装elasticdump非常方便。node官网:传送门https://nodejs.org/en下载版本:node-v12.14.0-linux-x64.tar.gz安装环境:centos7.9创建nodejs文件夹cd/usr/localmkdirnodejs解压文件tar-xzvfnode-v12.14.0-linux-x64.tar.gz移动nodejs文件mvno......
  • Linux之shell脚本
    目录一、shell脚本基础1.1shell的作用1.1.1shell脚本的概念1.1.2shell脚本应用场景1.1.3shell的作用--命令解释器1.1.4用户登陆的shell1.2shell脚本的构成1.3shell脚本的执行逻辑和方式1.4脚本错误调试1.5重定向与管道符1.5.1充定向1.5.2管道符二、shell脚本的变量2.1命名要求2.2r......
  • 多线程(三)
    1、线程安全的懒汉式1.1、线程安全的懒汉式,代码如下://一个单一设计模式的类如下:publicclassBank{privatedoubleaccount;privateStringname;privatestaticBankinstance=null;//私有化构造器privateBank(){}privateBank(dou......
  • 并发编程三要素:共享数据、互斥访问和同步机制
    「java、python面试题」来自UC网盘app分享,打开手机app,额外获得1T空间https://drive.uc.cn/s/2aeb6c2dcedd4https://drive.uc.cn/s/6077fc42116d4引言在现代计算机系统中,多线程并发编程已经成为了一种常见的编程范式。并发编程可以充分利用多核处理器的计算能力,提高程序的执......
  • Linux系统多网卡多网段多路由表配置
    Linux多个网卡多个网段存在的问题:1.由于只能配置一个默认网关,所以另外一个口只能配置路由,配置比较复杂;2.如果不配置的话,会存在往返路由不一致的情况,导致网络不通。所以,我们可以通过设置多个路由表的方式来实现源进源出,简单写下步骤:1.nano/etc/iproute2/rt_tables,增加两个网卡(vlan)......
  • 解决window移植到linux shell执行Python脚本提示找不到模块问题
    1、将工程目录添加到sys.path中(测试有效importsyscpath='project_path'#写成项目的地址最好是绝对地址因为有的地方确实会报错不清楚原因sys.path.append(cpath) eg:sys_path=os.path.abspath(os.curdir)sys.path.append(sys_path.split('test_case')[0])#为了......
  • linux手动安装.net5
    安装依赖sudoapt-getinstall-y-no-install-recommendslibc6libgcc1libgssapi-krb5-2libicu7libssl1.1libstdc++6zliblg解压官网下载的文件tarzxfdotnet-sdk-7.0.100-linux-x64.tar.gz配置环境变量vim~/.bashrc添加dotnet配置exportDOTNET_ROOT=\(HO......
  • ubuntu linux 安装与卸载 anaconda
    Anaconda安装step1下载并上传Anaconda安装包step2安装Anaconda注意到当前没有解压权限,所以要修改文件权限chmod+xAnaconda3-2023.07-2-Linux-x86_64.sh然后执行安装程序./Anaconda3-2023.07-2-Linux-x86_64.sh然后按照提示输入回车或者yes即可。其中有一步要注......