当前位置:网站首页>线程同步之条件变量
线程同步之条件变量
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,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。
边栏推荐
- 1. foundation closing
- Uni app, the text implementation expands and retracts the full text
- C # knowledge structure
- xml 解析bean工具类
- Oracle技术分享 oracle 19.14升级19.15
- 而是互联网开始有了新的进化,开始以一种全新的状态出现
- Prism framework
- Slide the menu of uni app custom components left and right and click switch to select and display in the middle
- Binary search method
- [Flink] a brief analysis of the writing process of Flink sort shuffle
猜你喜欢

Getting started with flask

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

Camera-CreateCaptureSession

第 4 篇:绘制四边形

【好书集锦】从技术到产品

(15)Blender源码分析之闪屏窗口显示菜单功能

Judge the same value of two sets 𞓜 different values

DETR3D 多2d图片3D检测框架

Uni app QR code scanning and identification function

How to solve the problem that iterative semi supervised training is difficult to implement in ASR training? RTC dev Meetup
随机推荐
[MySQL] MySQL export database
Go time package: second, millisecond, nanosecond timestamp output
ABP framework Practice Series (II) - Introduction to domain layer
2022.6.20-----leetcode. seven hundred and fifteen
[appium stepping pit] io appium. uiautomator2. common. exceptions. InvalidArgumentException: ‘capabilities‘ are mand
bubble sort
Camera-CreateCaptureSession
chrome页面录制,重放功能
Contains an object field at offset position
When the tiflash function is pushed down, it must be known that it will become a tiflash contributor in ten minutes
外包干了四年,人直接废了。。。
MySQL高级篇第一章(linux下安装MySQL)【下】
ASP. Net core introduction
MySQL advanced part (IV: locking mechanism and SQL optimization)
High performance computing center roce overview
816. 模糊坐标
1.基础关
Alibaba cloud function computing service one click to build Z-blog personal blog
软件调试测试的十大重要基本准则
(15) Blender source code analysis flash window display menu function