Linux系统编程—条件变量

条件变量是用来等待线程而不是上锁的,条件变量一般和互斥锁一块儿使用。条件变量之因此要和互斥锁一块儿使用,主要是由于互斥锁的一个明显的特色就是它只有两种状态:锁定和非锁定,而条件变量能够经过容许线程阻塞和等待另外一个线程发送信号来弥补互斥锁的不足,因此互斥锁和条件变量一般一块儿使用。程序员

当条件知足的时候,线程一般解锁并等待该条件发生变化,一旦另外一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将从新上锁,并测试条件是否知足。通常来讲条件变量被用于线程间的同步;当条件不知足的时候,容许其中的一个执行流挂起和等待。面试

简而言之,条件变量自己不是锁,但它也能够形成线程阻塞,一般与互斥锁配合使用,给多线程提供一个会合的场所。多线程

条件变量的优势:架构

相较于mutex而言,条件变量能够减小竞争。若是仅仅是mutex,那么,无论共享资源里有没数据,生产者及全部消费都全一窝蜂的去抢锁,会形成资源的浪费。函数

如直接使用mutex,除了生产者、消费者之间要竞争互斥量之外,消费者之间也须要竞争互斥量,但若是汇聚(链表)中没有数据,消费者之间竞争互斥锁是无心义的。有了条件变量机制之后,只有生产者完成生产,才会引发消费者之间的竞争。提升了程序效率。学习

主要应用函数:测试

pthread_cond_init函数
pthread_cond_destroy函数
pthread_cond_wait函数
pthread_cond_timedwait函数
pthread_cond_signal函数
pthread_cond_broadcast函数网站

以上6 个函数的返回值都是:成功返回0, 失败直接返回错误号。人工智能

pthread_cond_t类型:用于定义条件变量,好比:pthread_cond_t cond;spa

pthread_cond_init函数

函数原型:

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

函数做用:

初始化一个条件变量

参数说明:

cond:条件变量,调用时应传&cond给该函数

attr:条件变量属性,一般传NULL,表示使用默认属性

也可使用静态初始化的方法,初始化条件变量:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

pthread_cond_destroy函数

函数原型:

int pthread_cond_destroy(pthread_cond_t *cond);

函数做用:

销毁一个条件变量

pthread_cond_wait函数

函数原型:

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

函数做用:

阻塞等待一个条件变量。具体而言有如下三个做用:

  1. 阻塞等待条件变量cond(参1)知足;
  2. 释放已掌握的互斥锁mutex(解锁互斥量)至关于pthread_mutex_unlock(&mutex);
  3. 当被唤醒,pthread_cond_wait函数返回时,解除阻塞并从新申请获取互斥锁

其中一、2.两步为一个原子操做。

pthread_cond_timedwait函数

函数原型:

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

函数做用:

限时等待一个条件变量

参数说明:

前两个比较好理解,重点说明第三个参数。

这里有个struct timespec结构体,能够在man sem_timedwait中查看。结构体原型以下:

struct timespec {

​ time_t tv_sec; / seconds / 秒

​ long tv_nsec; / nanosecondes/ 纳秒

}

struct timespec定义的形参abstime是个绝对时间。注意,是绝对时间,不是相对时间。什么是绝对时间?2018年10月1日10:10:00,这就是一个绝对时间。什么是相对时间?给洗衣机定时30分钟洗衣服,就是一个相对时间,也就是说从当时时间开始计算30分钟,诸如此类。

如:time(NULL)返回的就是绝对时间。而alarm(1)是相对时间,相对当前时间定时1秒钟。

adstime所相对的时间是相对于1970年1月1日00:00:00,也就是UNIX计时元年。

下面给出一个错误用法:
struct timespec t = {1, 0};
pthread_cond_timedwait (&cond, &mutex, &t);
这种用法只能定时到 1970年1月1日 00:00:01秒,想必这个时间你们都还没出生。

正确用法:
time_t cur = time(NULL); 获取当前时间。
struct timespec t; 定义timespec 结构体变量t
t.tv_sec = cur+1; 定时1秒
pthread_cond_timedwait (&cond, &mutex, &t); 传参

pthread_cond_signal函数

函数原型:
int pthread_cond_signal(pthread_cond_t *cond);

函数做用:
唤醒至少一个阻塞在条件变量上的线程

pthread_cond_broadcast函数

函数原型:
int pthread_cond_broadcast(pthread_cond_t *cond);

函数做用:
唤醒所有阻塞在条件变量上的线程

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

不论是什么语言,只要提到线程同步,一个典型的案例就是生产者消费者模型。在Linux环境下,借助条件变量来实现这一模型,是比较常见的一种方法。

假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操做一个共享资源(通常称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。

看以下示例,使用条件变量模拟生产者、消费者问题:

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

typedef struct msg {
    struct msg *next;
    int num;
}msg_t;

msg_t *head = NULL;
msg_t *mp = NULL;

/* 静态初始化 一个条件变量 和 一个互斥量*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;

void *th_producer(void *arg)
{
    while (1) {
        mp = malloc(sizeof(msg_t));
        mp->num = rand() % 1000;        //模拟生产一个产品
        printf("--- produce: %d --------\n", mp->num);

        pthread_mutex_lock(&mutex);
        mp->next = head;
        head = mp;
        pthread_mutex_unlock(&mutex);

        pthread_cond_signal(&has_product);      //唤醒线程去消费产品
        sleep(rand() % 5);
    }
    return NULL;
}

void *th_consumer(void *arg)
{
    while (1) {
        pthread_mutex_lock(&mutex);
        while (head == NULL) {      //若是链表里没有产品,就没有抢锁的必要,一直阻塞等待
            pthread_cond_wait(&has_product, &mutex);
        }
        mp = head;
        head = mp->next;        //模拟消费掉一个产品
        pthread_mutex_unlock(&mutex);

        printf("========= consume: %d ======\n", mp->num);
        free(mp);
        mp = NULL;
        sleep(rand() % 5);
    }
    return NULL;
}

int main()
{
    pthread_t pid, cid;
    srand(time(NULL));

    pthread_create(&pid, NULL, th_producer, NULL);
    pthread_create(&cid, NULL, th_consumer, NULL);

    pthread_join(pid, NULL);
    pthread_join(cid, NULL);
    return 0;
}

运行结果:

更多精彩内容,请关注公众号良许Linux,公众内回复1024可免费得到5T技术资料,包括:Linux,C/C++,Python,树莓派,嵌入式,Java,人工智能,等等。公众号内回复进群,邀请您进高手如云技术交流群。

img


最后,最近不少小伙伴找我要Linux学习路线图,因而我根据本身的经验,利用业余时间熬夜肝了一个月,整理了一份电子书。不管你是面试仍是自我提高,相信都会对你有帮助!

免费送给你们,只求你们金指给我点个赞!

电子书 | Linux开发学习路线图

也但愿有小伙伴能加入我,把这份电子书作得更完美!

有收获?但愿老铁们来个三连击,给更多的人看到这篇文章

推荐阅读:

相关文章
相关标签/搜索