关于linux:Linux系统编程条件变量

4次阅读

共计 3842 个字符,预计需要花费 10 分钟才能阅读完成。

条件变量是用来期待线程而不是上锁的,条件变量通常和互斥锁一起应用。条件变量之所以要和互斥锁一起应用,次要是因为互斥锁的一个显著的特点就是它只有两种状态:锁定和非锁定,而条件变量能够通过容许线程阻塞和期待另一个线程发送信号来补救互斥锁的有余,所以互斥锁和条件变量通常一起应用。

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

简而言之,条件变量自身不是锁,但它也能够造成线程阻塞,通常与互斥锁配合应用,给多线程提供一个会合的场合。

条件变量的长处:

相较于 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;

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 函数返回时,解除阻塞并从新申请获取互斥锁

其中 1、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,人工智能 ,等等。公众号内回复 进群,邀请您进高手如云技术交换群。


最初,最近很多小伙伴找我要Linux 学习路线图,于是我依据本人的教训,利用业余时间熬夜肝了一个月,整顿了一份电子书。无论你是面试还是自我晋升,置信都会对你有帮忙!

收费送给大家,只求大家金指给我点个赞!

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

也心愿有小伙伴能退出我,把这份电子书做得更完满!

有播种?心愿老铁们来个三连击,给更多的人看到这篇文章

举荐浏览:

  • 干货 | 程序员进阶架构师必备资源免费送
  • 神器 | 反对搜寻的资源网站
正文完
 0