Linux线程同步(条件变量)

简介: Linux线程同步(条件变量)

前言

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

一、条件变量概念

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

总结

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


相关文章
|
2月前
|
算法 Unix Linux
linux线程调度策略
linux线程调度策略
55 0
|
2月前
|
Java 开发者
解锁并发编程新姿势!深度揭秘AQS独占锁&ReentrantLock重入锁奥秘,Condition条件变量让你玩转线程协作,秒变并发大神!
【8月更文挑战第4天】AQS是Java并发编程的核心框架,为锁和同步器提供基础结构。ReentrantLock基于AQS实现可重入互斥锁,比`synchronized`更灵活,支持可中断锁获取及超时控制。通过维护计数器实现锁的重入性。Condition接口允许ReentrantLock创建多个条件变量,支持细粒度线程协作,超越了传统`wait`/`notify`机制,助力开发者构建高效可靠的并发应用。
77 0
|
1月前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
1月前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。
|
2月前
|
存储 设计模式 NoSQL
Linux线程详解
Linux线程详解
|
2月前
|
缓存 Linux C语言
Linux线程是如何创建的
【8月更文挑战第5天】线程不是一个完全由内核实现的机制,它是由内核态和用户态合作完成的。
|
2月前
|
Linux Shell
在Linux中,如何将二进制文件添加到 $PATH 变量中?
在Linux中,如何将二进制文件添加到 $PATH 变量中?
|
3月前
|
存储 SQL Java
(七)全面剖析Java并发编程之线程变量副本ThreadLocal原理分析
在之前的文章:彻底理解Java并发编程之Synchronized关键字实现原理剖析中我们曾初次谈到线程安全问题引发的"三要素":多线程、共享资源/临界资源、非原子性操作,简而言之:在同一时刻,多条线程同时对临界资源进行非原子性操作则有可能产生线程安全问题。
|
3月前
|
Java Linux
linux 对子用户配置java 环境变量
linux 对子用户配置java 环境变量
29 3
|
2月前
|
负载均衡 Linux 调度
在Linux中,进程和线程有何作用?
在Linux中,进程和线程有何作用?