当前位置:网站首页>线程同步之条件变量
线程同步之条件变量
2022-06-26 03:56:00 【StudyWinter】
1 基本概念
条件变量本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。
条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用。
当条件满足的时候,线程通常解锁并等待该条件发生变化,一旦另一个线程修改了环境变量,就会通知相应的环境变量唤醒一个或者多个被这个条件变量阻塞的线程。这些被唤醒的线程将重新上锁,并测试条件是否满足。一般来说条件变量被用于线程间的同步;当条件不满足的时候,允许其中的一个执行流挂起和等待
2 为什么使用条件变量
线程抢占互斥锁时,线程A抢到了互斥锁,但是条件不满足,线程A就会让出互斥锁让给其他线程,然后等待其他线程唤醒他;一旦条件满足,线程就可以被唤醒,并且拿互斥锁去访问共享区。经过这中设计能让进程运行更稳定。
3 函数使用
3.1 pthread_cond_init函数
作用:初始化一个条件变量
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
// 参 2:attr 表条件变量属性,通常为默认值,传 NULL 即可静态初始化和动态初始化
// 1 静态初始化
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
// 2 动态初始化
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);3.2 pthread_cond_destroy函数
作用:销毁一个条件变量
int pthread_cond_destroy(pthread_cond_t *cond);3.3 pthread_cond_wait函数(重点)
作用:(非常重要 三点)
1 阻塞等待条件变量 cond(参 1)满足
2 释放已掌握的互斥锁(解锁互斥量)相当于 pthread_mutex_unlock(&mutex);
3 当被唤醒,pthread_cond_wait 函数返回时,解除阻塞并重新申请获取互斥锁
pthread_mutex_lock(&mutex);
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);1.2.两步为一个原子操作。
3.4 pthread_cond_timedwait 函数
作用:限时等待一个条件变量
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);3.5 pthread_cond_signal 函数
作用:唤醒至少一个阻塞在条件变量上的线程
int pthread_cond_signal(pthread_cond_t *cond);3.6 pthread_cond_broadcast 函数
作用:唤醒全部阻塞在条件变量上的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
4 生产者消费者模型

步骤:
生产者:
(1)生产数据;
(2)加锁pthread_mutex_lock(&mutex);
(3)将数据放置到公共区域;
(4)解锁pthread_mutex_unlock(&mutex);
(5)通知阻塞在条件变量上的线程pthread_cond_signal()、pthread_cond_brocadcast();
(6)循环产生后序数据。
消费者:
(1)创建锁pthread_mutex_t mutex;
(2)初始化锁pthread_mutex_init(mutex, NULL);
(3)加锁pthread_mutex_lock(&mutex);
(4)等待条件满足
pthread_cond_wait(&cond, &mutex);
阻塞等待条件变量;
解锁;
----10s;
加锁;
(5)访问共享数据;
(6)解锁、释放条件变量,释放锁。
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
// 借助条件变量模拟[生产者-消费者]问题
// 链表作为共享数据,需要被互斥量保护
struct Msg
{
int val;
struct Msg *next;
};
// 头节点
struct Msg *head;
// 静态初始化互斥量和条件变量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
// 生产者线程
void *producer(void *arg)
{
struct Msg *mp;
while (1)
{
// 申请空间
mp = malloc(sizeof(struct Msg));
// 模拟生产一个产品
mp->val = rand() % 1000 + 1;
printf("---produce----------------------:%d\n", mp->val);
// 加锁
int res = pthread_mutex_lock(&lock);
if (res != 0)
{
fprintf(stderr, "pthread_mutex_lock producer error:%s\n", strerror(res));
exit(1);
}
// 头插法
mp->next = head;
head = mp;
// 解锁
res = pthread_mutex_unlock(&lock);
if (res != 0)
{
fprintf(stderr, "pthread_mutex_unlock producer error:%s\n", strerror(res));
exit(1);
}
// 将等待在条件变量上的一个线程唤醒
res = pthread_cond_signal(&has_product);
if (res != 0)
{
fprintf(stderr, "pthread_cond_signal producer error:%s\n", strerror(res));
exit(1);
}
sleep(rand() % 5);
}
}
// 消费者线程
void *consumer(void *arg)
{
struct Msg *mp;
while (1)
{
// 加锁
int res = pthread_mutex_lock(&lock);
if (res != 0)
{
fprintf(stderr, "pthread_mutex_lock consumer error:%s\n", strerror(res));
exit(1);
}
// 头节点为空,说明没有节点
while (head == NULL)
{
// 消费者在阻塞
res = pthread_cond_wait(&has_product, &lock);
if (res != 0)
{
fprintf(stderr, "pthread_cond_wait consumer error:%s\n", strerror(res));
exit(1);
}
}
// 模拟消费掉一个产品
mp = head;
head = head->next;
// 解锁
res = pthread_mutex_unlock(&lock);
if (res != 0)
{
fprintf(stderr, "pthread_mutex_unlock consumer error:%s\n", strerror(res));
exit(1);
}
printf("==Consumer:%lu====%d\n", pthread_self(), mp->val);
// 释放
free(mp);
sleep(rand() % 5);
}
}
int main(int argc, char **argv)
{
// 创建生产者线程和消费者线程
pthread_t pid, cid;
srand(time(NULL));
// 创建生产者线程
int res = pthread_create(&pid, NULL, producer, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_create producer error:%s\n", strerror(res));
exit(1);
}
// 创建消费者线程
res = pthread_create(&cid, NULL, consumer, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_create consumer error:%s\n", strerror(res));
exit(1);
}
// 回收生产者线程
res = pthread_join(pid, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_join producer error:%s\n", strerror(res));
exit(1);
}
// 回收消费者线程
res = pthread_join(cid, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_join consumer error:%s\n", strerror(res));
exit(1);
}
return 0;
}执行

5 多个消费者
生产者休息时间短一些,其他都一样
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
// 借助条件变量模拟[生产者-消费者]问题
// 链表作为共享数据,需要被互斥量保护
struct Msg
{
int val;
struct Msg *next;
};
// 头节点
struct Msg *head;
// 静态初始化互斥量和条件变量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
// 生产者线程
void *producer(void *arg)
{
struct Msg *mp;
while (1)
{
// 申请空间
mp = malloc(sizeof(struct Msg));
// 模拟生产一个产品
mp->val = rand() % 1000 + 1;
printf("---produce----------------------:%d\n", mp->val);
// 加锁
int res = pthread_mutex_lock(&lock);
if (res != 0)
{
fprintf(stderr, "pthread_mutex_lock producer error:%s\n", strerror(res));
exit(1);
}
// 头插法
mp->next = head;
head = mp;
// 解锁
res = pthread_mutex_unlock(&lock);
if (res != 0)
{
fprintf(stderr, "pthread_mutex_unlock producer error:%s\n", strerror(res));
exit(1);
}
// 将等待在条件变量上的一个线程唤醒
res = pthread_cond_signal(&has_product);
if (res != 0)
{
fprintf(stderr, "pthread_cond_signal producer error:%s\n", strerror(res));
exit(1);
}
sleep(rand() % 3);
}
}
// 消费者线程
void *consumer(void *arg)
{
struct Msg *mp;
while (1)
{
// 加锁
int res = pthread_mutex_lock(&lock);
if (res != 0)
{
fprintf(stderr, "pthread_mutex_lock consumer error:%s\n", strerror(res));
exit(1);
}
// 头节点为空,说明没有节点
while (head == NULL)
{
// 消费者在阻塞
res = pthread_cond_wait(&has_product, &lock);
if (res != 0)
{
fprintf(stderr, "pthread_cond_wait consumer error:%s\n", strerror(res));
exit(1);
}
}
// 模拟消费掉一个产品
mp = head;
head = head->next;
// 解锁
res = pthread_mutex_unlock(&lock);
if (res != 0)
{
fprintf(stderr, "pthread_mutex_unlock consumer error:%s\n", strerror(res));
exit(1);
}
printf("==Consumer:%lu====%d\n", pthread_self(), mp->val);
// 释放
free(mp);
sleep(rand() % 5);
}
}
int main(int argc, char **argv)
{
// 创建生产者线程和消费者线程
pthread_t pid, cid;
srand(time(NULL));
// 创建生产者线程
int res = pthread_create(&pid, NULL, producer, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_create producer error:%s\n", strerror(res));
exit(1);
}
/***********3个消费者****************/
// 创建消费者线程
res = pthread_create(&cid, NULL, consumer, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_create consumer error:%s\n", strerror(res));
exit(1);
}
// 创建消费者线程
res = pthread_create(&cid, NULL, consumer, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_create consumer error:%s\n", strerror(res));
exit(1);
}
// 创建消费者线程
res = pthread_create(&cid, NULL, consumer, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_create consumer error:%s\n", strerror(res));
exit(1);
}
// 回收生产者线程
res = pthread_join(pid, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_join producer error:%s\n", strerror(res));
exit(1);
}
// 回收消费者线程
res = pthread_join(cid, NULL);
if (res != 0)
{
fprintf(stderr, "pthread_join consumer error:%s\n", strerror(res));
exit(1);
}
return 0;
}执行

6 条件变量的优点
相较于 mutex 而言,条件变量可以减少竞争。
如直接使用 mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。
边栏推荐
猜你喜欢

After a test of 25K bytes, I really saw the basic ceiling

ABP framework Practice Series (II) - Introduction to domain layer

Yolov5 improvements: replace the backbone

Non H5 end of uni app, regional setting of status bar on the top of mobile phone

Oracle technology sharing Oracle 19.14 upgrade 19.15

面了个字节拿25k出来的测试,算是真正见识到了基础的天花板

ABP framework Practice Series (III) - domain layer in depth

使用SOAPUI访问对应的esb工程

C # knowledge structure

ABP framework Practice Series (I) - Introduction to persistence layer
随机推荐
在出海获客这件事上,数字广告投放之外,广告主还能怎么玩儿?
使用SOAPUI访问对应的esb工程
Binary search method
Oracle技术分享 oracle 19.14升级19.15
机器学习笔记 - 时间序列的趋势分量
Pay attention to the entrance components of official account in the applet
Kotlin learning apply plugin: 'kotlin Android extensions‘
[MySQL] MySQL export database
[appium stepping pit] io appium. uiautomator2. common. exceptions. InvalidArgumentException: ‘capabilities‘ are mand
【LOJ#6718】九个太阳「弱」化版(循环卷积,任意模数NTT)
Part 4: drawing quadrilateral
Camera-CreateCaptureSession
Open source! Vitae model brushes the world's first again: the new coco human posture estimation model achieves the highest accuracy of 81.1ap
Concept and implementation of QPS
asp.net网页选择身份进行登录的简单代码,asp连接数据库,使用asp:Panel、asp:DropDownList控件
Quanergy欢迎Lori Sundberg出任首席人力资源官
Kotlin uses viewpager2+fragment+bottomnavigationview to implement the style of the switching module of the bottom menu bar.
Non H5 end of uni app, regional setting of status bar on the top of mobile phone
Camera-memory内存泄漏分析(二)
Unity移动端游戏性能优化简谱之 以引擎模块为划分的CPU耗时调优