小阳同学嵌入式学习日记-条件变量

本文详细介绍了条件变量的概念、应用函数及其在生产者消费者模型中的运用,强调了其在减少资源竞争、提高系统效率和可读性方面的优势。
摘要由CSDN通过智能技术生成

 

目录

一、什么是条件变量? 

二、条件变量的应用函数

a、pthread_cond_init()

b、pthread_cond_wait()

c、 pthread_cond_timedwait()

 d、pthread_cond_signal()

e、pthread_cond_broadcast()

f、 pthread_cond_destroy()

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

四、条件变量的优点 


一、什么是条件变量? 

        条件变量(Condition Variables)是一种线程同步的机制,用于在多线程编程中实现线程之间的协作。它通常与互斥锁(Mutex)一起使用,用于在线程等待某个条件成立时暂停执行,并在条件满足时恢复执行。条件变量允许线程在等待某个条件成立时进入阻塞状态,以避免忙等待(busy-waiting)的情况,从而有效地利用系统资源。

        条件变量通常提供了两种操作:

  1. 等待(wait):一个线程可以调用等待操作来阻塞自己的执行,直到某个条件被满足。当某个线程在条件不满足时调用等待操作时,它会释放持有的互斥锁,并进入阻塞状态等待条件成立。一旦条件成立,其他线程调用通知操作时,等待线程会被唤醒,并重新尝试获取互斥锁以继续执行。
  2. 通知(notify):一个线程可以调用通知操作来通知等待在条件上的其他线程,条件已经被满足,因此它们可以尝试重新竞争获取互斥锁。通知操作通常有两种形式:通知单个线程(notify_one)和通知所有线程(notify_all)。

二、条件变量的应用函数

a、pthread_cond_init()

        pthread_cond_init() 函数用于初始化一个条件变量,其函数原型如下:

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
  • cond指向要初始化的条件变量的指针。
  • attr条件变量的属性,通常传入 NULL 表示使用默认属性。

        这个函数用于在使用条件变量之前对其进行初始化。初始化后的条件变量可以在多个线程之间共享使用。如果条件变量在栈上分配内存,那么在使用之前必须对其进行初始化。注意,这个函数不会分配内存,只是对已分配的条件变量进行初始化。 

返回值:

  • 成功:返回 0。
  • 失败:返回一个正整数的错误码,表示发生了错误。

示例用法:

pthread_cond_t my_cond;

int main() {
    // 初始化条件变量
    if (pthread_cond_init(&my_cond, NULL) != 0) {
        perror("pthread_cond_init");
        exit(EXIT_FAILURE);
    }

    // 使用 my_cond 进行其他操作...

    // 销毁条件变量
    pthread_cond_destroy(&my_cond);

    return 0;
}

         这个函数是 POSIX 线程库中对条件变量的初始化操作。

b、pthread_cond_wait()

         pthread_cond_wait() 函数用于阻塞等待一个条件变量,其函数原型如下:

  • cond指向要等待的条件变量的指针。
  • mutex指向与条件变量关联的互斥锁的指针。在调用该函数之前,线程必须先锁定该互斥锁,否则会导致未定义的行为。在调用完 pthread_cond_wait() 后,互斥锁会被释放,以允许其他线程访问共享资源。

         该函数被调用时,线程会被阻塞,直到其他线程调用 pthread_cond_signal()pthread_cond_broadcast() 函数来通知条件变量,并且该线程获得了与条件变量关联的互斥锁。在被唤醒之后,该线程会重新获得互斥锁并继续执行。

 返回值:

  • 成功:返回 0。
  • 失败:返回一个正整数的错误码,表示发生了错误。

示例用法:

pthread_cond_t my_cond;
pthread_mutex_t my_mutex;
int shared_data = 0;

void* thread_function(void* arg) {
    pthread_mutex_lock(&my_mutex);
    while (shared_data == 0) {
        pthread_cond_wait(&my_cond, &my_mutex);
    }
    // 在这里处理共享资源
    pthread_mutex_unlock(&my_mutex);
    return NULL;
}

int main() {
    // 初始化条件变量和互斥锁
    pthread_cond_init(&my_cond, NULL);
    pthread_mutex_init(&my_mutex, NULL);

    // 创建线程并执行
    pthread_t tid;
    pthread_create(&tid, NULL, thread_function, NULL);

    // 修改共享资源
    pthread_mutex_lock(&my_mutex);
    shared_data = 1;
    // 通知等待的线程
    pthread_cond_signal(&my_cond);
    pthread_mutex_unlock(&my_mutex);

    // 等待线程执行完成
    pthread_join(tid, NULL);

    // 销毁条件变量和互斥锁
    pthread_cond_destroy(&my_cond);
    pthread_mutex_destroy(&my_mutex);

    return 0;
}

         这个函数允许线程等待条件变量的发生,通常与互斥锁结合使用来实现线程间的同步。

c、 pthread_cond_timedwait()

         pthread_cond_timedwait() 函数与 pthread_cond_wait() 函数类似,但是它允许设置一个超时时间,如果超过了指定的时间仍然没有收到条件变量的信号,则函数返回。其函数原型如下:

#include <pthread.h>
#include <time.h>

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
  • cond指向要等待的条件变量的指针。
  • mutex指向与条件变量关联的互斥锁的指针。在调用该函数之前,线程必须先锁定该互斥锁,否则会导致未定义的行为。在调用完 pthread_cond_timedwait() 后,互斥锁会被释放,以允许其他线程访问共享资源。
  • abstime指向一个 timespec 结构体的指针,表示等待的超时时间。

        该函数会阻塞当前线程,直到条件变量被信号唤醒或者超时。如果超时时间到达但条件变量仍未被信号唤醒,该函数会返回 ETIMEDOUT 错误码。

返回值:

  • 成功:返回 0。
  • 失败:返回一个正整数的错误码,表示发生了错误。

示例用法:

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

pthread_cond_t my_cond;
pthread_mutex_t my_mutex;
int shared_data = 0;

void* thread_function(void* arg) {
    pthread_mutex_lock(&my_mutex);
    struct timespec timeout;
    clock_gettime(CLOCK_REALTIME, &timeout);
    timeout.tv_sec += 5; // 设置超时时间为5秒后
    int result = pthread_cond_timedwait(&my_cond, &my_mutex, &timeout);
    if (result == ETIMEDOUT) {
        printf("Timed out waiting for condition\n");
    } else if (result == 0) {
        printf("Condition signaled\n");
    } else {
        perror("pthread_cond_timedwait");
    }
    pthread_mutex_unlock(&my_mutex);
    return NULL;
}

int main() {
    // 初始化条件变量和互斥锁
    pthread_cond_init(&my_cond, NULL);
    pthread_mutex_init(&my_mutex, NULL);

    // 创建线程并执行
    pthread_t tid;
    pthread_create(&tid, NULL, thread_function, NULL);

    // 等待一段时间后发送信号
    sleep(7);
    pthread_mutex_lock(&my_mutex);
    shared_data = 1;
    // 通知等待的线程
    pthread_cond_signal(&my_cond);
    pthread_mutex_unlock(&my_mutex);

    // 等待线程执行完成
    pthread_join(tid, NULL);

    // 销毁条件变量和互斥锁
    pthread_cond_destroy(&my_cond);
    pthread_mutex_destroy(&my_mutex);

    return 0;
}

        这个函数允许设置一个超时时间,在超时之后会返回,可以用于实现一些需要等待一段时间的操作。

 d、pthread_cond_signal()

         pthread_cond_signal() 函数用于唤醒至少一个等待在条件变量上的线程。其函数原型如下:

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);
  • cond指向要发送信号的条件变量的指针。

         该函数调用后会唤醒至少一个等待在条件变量上的线程。如果没有线程在等待条件变量上,那么调用 pthread_cond_signal() 函数也不会有任何作用。

返回值:

  • 成功:返回 0。
  • 失败:返回一个正整数的错误码,表示发生了错误。

示例用法:

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

pthread_cond_t my_cond;
pthread_mutex_t my_mutex;
int shared_data = 0;

void* thread_function(void* arg) {
    pthread_mutex_lock(&my_mutex);
    printf("Waiting for condition...\n");
    pthread_cond_wait(&my_cond, &my_mutex);
    printf("Condition signaled, shared_data: %d\n", shared_data);
    pthread_mutex_unlock(&my_mutex);
    return NULL;
}

int main() {
    // 初始化条件变量和互斥锁
    pthread_cond_init(&my_cond, NULL);
    pthread_mutex_init(&my_mutex, NULL);

    // 创建线程并执行
    pthread_t tid;
    pthread_create(&tid, NULL, thread_function, NULL);

    // 修改共享资源
    pthread_mutex_lock(&my_mutex);
    shared_data = 1;
    // 通知等待的线程
    pthread_cond_signal(&my_cond);
    pthread_mutex_unlock(&my_mutex);

    // 等待线程执行完成
    pthread_join(tid, NULL);

    // 销毁条件变量和互斥锁
    pthread_cond_destroy(&my_cond);
    pthread_mutex_destroy(&my_mutex);

    return 0;
}

         这个函数用于在满足条件时唤醒一个或多个等待线程,以便它们可以继续执行。

e、pthread_cond_broadcast()

        pthread_cond_broadcast() 函数用于唤醒所有等待在条件变量上的线程。其函数原型如下:

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);
  • cond指向要广播信号的条件变量的指针。

        该函数会唤醒所有等待在条件变量上的线程。如果没有线程在等待条件变量上,调用 pthread_cond_broadcast() 函数也不会有任何作用。

返回值:

  • 成功:返回 0。
  • 失败:返回一个正整数的错误码,表示发生了错误。

示例用法:

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

pthread_cond_t my_cond;
pthread_mutex_t my_mutex;
int shared_data = 0;

void* thread_function(void* arg) {
    pthread_mutex_lock(&my_mutex);
    printf("Waiting for condition...\n");
    pthread_cond_wait(&my_cond, &my_mutex);
    printf("Condition signaled, shared_data: %d\n", shared_data);
    pthread_mutex_unlock(&my_mutex);
    return NULL;
}

int main() {
    // 初始化条件变量和互斥锁
    pthread_cond_init(&my_cond, NULL);
    pthread_mutex_init(&my_mutex, NULL);

    // 创建线程并执行
    pthread_t tid;
    pthread_create(&tid, NULL, thread_function, NULL);

    // 修改共享资源
    pthread_mutex_lock(&my_mutex);
    shared_data = 1;
    // 广播通知所有等待的线程
    pthread_cond_broadcast(&my_cond);
    pthread_mutex_unlock(&my_mutex);

    // 等待线程执行完成
    pthread_join(tid, NULL);

    // 销毁条件变量和互斥锁
    pthread_cond_destroy(&my_cond);
    pthread_mutex_destroy(&my_mutex);

    return 0;
}

        这个函数用于在满足条件时唤醒所有等待线程,以便它们可以继续执行。

f、 pthread_cond_destroy()

         pthread_cond_destroy() 函数用于销毁一个条件变量,释放相关资源。其函数原型如下:

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cond);
  • cond指向要销毁的条件变量的指针。

        该函数会销毁指定的条件变量,释放与其相关的资源。在调用该函数之后,不能再对该条件变量进行任何操作,否则将导致未定义的行为。

返回值:

  • 成功:返回 0。
  • 失败:返回一个正整数的错误码,表示发生了错误。

示例用法:

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

pthread_cond_t my_cond;

int main() {
    // 初始化条件变量
    pthread_cond_init(&my_cond, NULL);

    // 使用条件变量...

    // 销毁条件变量
    if (pthread_cond_destroy(&my_cond) != 0) {
        perror("pthread_cond_destroy");
        exit(EXIT_FAILURE);
    }

    return 0;
}

         这个函数用于释放条件变量相关的资源,通常在不再需要条件变量时调用。

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

        生产者-消费者问题是一个经典的多线程同步问题,其中生产者线程负责生产数据并将其放入共享缓冲区,而消费者线程则负责从缓冲区中取出数据进行处理。条件变量是实现生产者-消费者模型的一种常见方法之一。

以下是使用条件变量实现生产者-消费者模型的基本步骤:

  1. 定义共享资源:通常是一个缓冲区,生产者向其中放入数据,消费者从中取出数据。
  2. 定义互斥锁:用于保护对共享资源的访问,防止多个线程同时对其进行操作。
  3. 定义条件变量:用于在缓冲区为空时阻塞消费者线程,在缓冲区满时阻塞生产者线程,直到条件满足时唤醒相应的线程。
  4. 编写生产者线程函数和消费者线程函数,分别进行生产和消费操作。
  5. 在生产者线程和消费者线程中使用互斥锁保护对共享资源的访问,并在必要时等待条件变量的信号。
  6. 在生产者线程向缓冲区放入数据后,通知等待在条件变量上的消费者线程。
  7. 在消费者线程从缓冲区取出数据后,通知等待在条件变量上的生产者线程。

以下是一个简单的生产者-消费者条件变量模型的示例(使用 C 语言和 POSIX 线程库):

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

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE]; // 共享缓冲区
int count = 0; // 缓冲区中当前项目的数量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 互斥锁
pthread_cond_t cond_full = PTHREAD_COND_INITIALIZER; // 缓冲区满条件变量
pthread_cond_t cond_empty = PTHREAD_COND_INITIALIZER; // 缓冲区空条件变量

// 生产者线程函数
void* producer(void* arg) {
    int item = 0;
    while (1) {
        pthread_mutex_lock(&mutex); // 加锁
        if (count == BUFFER_SIZE) { // 如果缓冲区满
            pthread_cond_wait(&cond_full, &mutex); // 等待缓冲区不满
        }
        buffer[count++] = item++; // 生产一个项目并放入缓冲区
        printf("Produced item %d\n", item);
        pthread_cond_signal(&cond_empty); // 唤醒等待缓冲区为空的消费者线程
        pthread_mutex_unlock(&mutex); // 解锁
    }
    return NULL;
}

// 消费者线程函数
void* consumer(void* arg) {
    while (1) {
        pthread_mutex_lock(&mutex); // 加锁
        if (count == 0) { // 如果缓冲区为空
            pthread_cond_wait(&cond_empty, &mutex); // 等待缓冲区不空
        }
        int item = buffer[--count]; // 从缓冲区取出一个项目
        printf("Consumed item %d\n", item);
        pthread_cond_signal(&cond_full); // 唤醒等待缓冲区不满的生产者线程
        pthread_mutex_unlock(&mutex); // 解锁
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    // 创建生产者线程和消费者线程
    pthread_create(&producer_thread, NULL, producer, NULL);
    pthread_create(&consumer_thread, NULL, consumer, NULL);

    // 等待线程结束
    pthread_join(producer_thread, NULL);
    pthread_join(consumer_thread, NULL);

    return 0;
}

         这段代码实现了一个简单的生产者-消费者模型,其中生产者线程不断向缓冲区放入数据,而消费者线程不断从缓冲区取出数据。通过互斥锁和条件变量来确保线程之间的同步和互斥访问共享资源。

运行结果如下:

四、条件变量的优点 

条件变量作为多线程编程中的一种同步机制,具有以下优点:

  1. 减少资源竞争: 条件变量可以使线程在等待某个条件时进入阻塞状态,而不是采用忙等待的方式,从而减少了对共享资源的竞争,提高了系统的性能和效率。

  2. 提高系统吞吐量: 使用条件变量可以允许线程在等待某个条件时挂起,当条件满足时再唤醒,这样可以更有效地利用系统资源,提高了系统的吞吐量。

  3. 避免忙等待: 忙等待会消耗大量的 CPU 时间,降低系统的效率,并且容易导致死锁等问题。条件变量的出现可以避免这种情况的发生,使得线程能够在条件满足时自动进入阻塞状态,直到条件被满足时被唤醒。

  4. 提高代码可读性和可维护性: 使用条件变量可以更清晰地表达线程之间的逻辑关系,使得代码更易于理解和维护。

  5. 支持复杂的线程间通信: 条件变量通常与互斥锁一起使用,可以支持更复杂的线程间通信和同步需求,例如生产者-消费者模型、读者-写者问题等。

        总的来说,条件变量是多线程编程中非常重要的一种同步机制,它能够有效地提高系统的性能和可维护性,减少资源竞争和忙等待,支持复杂的线程间通信,是实现多线程程序的重要工具之一。

做大做强,再创辉煌! 

  • 33
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值