IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 第30章 条件变量 -> 正文阅读

[嵌入式]第30章 条件变量

,在很多情况下,线程需要检查某一条件(condition)满足之后,才会继续运行。例如,父线程需要检查子线程是否执行完毕 [这常被称为 join()]。这种等待如何实现呢?

我们可以尝试用一个共享变量,如图 30.2 所示。这种解决方案一般能工作,但是效率低下,因为主线程会自旋检查,浪费 CPU 时间。我们希望有某种方式让父线程休眠,直到等待的条件满足(即子线程完成执行)。

 volatile int done = 0;

 void *child(void *arg) {
	 printf("child\n");
	 done = 1;
	 return NULL;
 }
 
 int main(int argc, char *argv[]) {
	 printf("parent: begin\n");
	 pthread_t c;
 	Pthread_create(&c, NULL, child, NULL); // create 	child
	while (done == 0)
		; // spin
	printf("parent: end\n");
	return 0;
}

30.1 定义和程序

线程可以使用条件变量(condition variable),来等待一个条件变成真。条件变量是一个显式队列,当某些执行状态(即条件,condition)不满足时,线程可以把自己加入队列,等待(waiting)该条件。。另外某个线程,当它改变了上述状态时,就可以唤醒一个或者多个等待线程(通过在该条件上发信号),让它们继续执行。Dijkstra 最早在“私有信号量”[D01]中提出这种思想。Hoare 后来在关于观察者的工作中,将类似的思想称为条件变量

pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m);
pthread_cond_signal(pthread_cond_t *c);
int done = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;

void thr_exit() {
	Pthread_mutex_lock(&m);
	done = 1;
	Pthread_cond_signal(&c);
	Pthread_mutex_unlock(&m);
}

void *child(void *arg) {
	printf("child\n");
	thr_exit();
	return NULL;
}

void thr_join() {
	Pthread_mutex_lock(&m);
	while (done == 0)
	Pthread_cond_wait(&c, &m);
	Pthread_mutex_unlock(&m);
}
int main(int argc, char *argv[]) {
	printf("parent: begin\n");
	pthread_t p;
	Pthread_create(&p, NULL, child, NULL);
	thr_join();
	printf("parent: end\n");
	return 0;
}

30.2 生产者/消费者(有界缓冲区)问题

假设有一个或多个生产者线程和一个或多个消费者线程。生产者把生成的数据项放入缓冲区;消费者从缓冲区取走数据项,以某种方式消费。

首先需要一个共享缓冲区,让生产者放入数据,消费者取出数据。简单起见,我们就拿一个整数来做缓冲区(你当然可以想到用一个指向数据结构的指针来代替) ,两个内部函数将值放入缓冲区,从缓冲区取值。如下:

int buffer;
int count = 0; // initially, empty
void put(int value) {
	assert(count == 0);
	count = 1;
	buffer = value;
}

int get() {
	assert(count == 1);
	count = 0;
	return buffer;
}

现在我们需要编写一些函数,知道何时可以访问缓冲区,以便将数据放入缓冲区或从缓冲区取出数据。条件是显而易见的:仅在 count 为 0 时(即缓冲器为空时),才将数据放入缓冲器中。仅在计数为 1 时(即缓冲器已满时),才从缓冲器获得数据。

void *producer(void *arg) {
	int i;
	int loops = (int) arg;
	for (i = 0; i < loops; i++) {
		put(i);
	}
}
void *consumer(void *arg) {
	int i;
	while (1) {
		int tmp = get();
		printf("%d\n", tmp);	
	}
}

有问题的方案

假设只有一个生产者和一个消费者。显然, put()和 get()函数之中会有临界区,因为 put()更新缓冲区,get()读取缓冲区。但是,给代码加锁没有用,我们还需别的东西。不奇怪,别的东西就是某些条件变量。

 cond_t cond;
 mutex_t mutex;

 void *producer(void *arg) {
 	int i;
 	for (i = 0; i < loops; i++) {
 		Pthread_mutex_lock(&mutex); // p1
 		if (count == 1)             // p2
 			Pthread_cond_wait(&cond, &mutex); // p3
 		put(i);					   // p4
 	    Pthread_cond_signal(&cond);// p5
 		Pthread_mutex_unlock(&mutex);//p6
 	}
 }

 void *consumer(void *arg) {
 	int i;
 	for (i = 0; i < loops; i++) {
 		Pthread_mutex_lock(&mutex); // c1
 		if (count == 0)             // c2
 			Pthread_cond_wait(&cond, &mutex); // c3
 		int tmp = get();            // c4
 		Pthread_cond_signal(&cond); // c5
 		Pthread_mutex_unlock(&mutex);// c6
		printf("%d\n", tmp);
	}
}

较好但仍有问题的方案:使用 While 语句替代 If

 cond_t cond;
 mutex_t mutex;

 void *producer(void *arg) {
 	int i;
 	for (i = 0; i < loops; i++) {
 		Pthread_mutex_lock(&mutex); // p1
 		while (count == 1)             // p2
 			Pthread_cond_wait(&cond, &mutex); // p3
 		put(i);					   // p4
 	    Pthread_cond_signal(&cond);// p5
 		Pthread_mutex_unlock(&mutex);//p6
 	}
 }

 void *consumer(void *arg) {
 	int i;
 	for (i = 0; i < loops; i++) {
 		Pthread_mutex_lock(&mutex); // c1
 		while (count == 0)             // c2
 			Pthread_cond_wait(&cond, &mutex); // c3
 		int tmp = get();            // c4
 		Pthread_cond_signal(&cond); // c5
 		Pthread_mutex_unlock(&mutex);// c6
		printf("%d\n", tmp);
	}
}

单值缓冲区的生产者/消费者方案

使用两个条件变量

 cond_t empty, fill;
 mutex_t mutex;

 void *producer(void *arg) {
 	int i;
 	for (i = 0; i < loops; i++) {
 		Pthread_mutex_lock(&mutex); // p1
 		while (count == 1)             // p2
 			Pthread_cond_wait(&empty, &mutex); // p3
 		put(i);					   // p4
 	    Pthread_cond_signal(&fill);// p5
 		Pthread_mutex_unlock(&mutex);//p6
 	}
 }

 void *consumer(void *arg) {
 	int i;
 	for (i = 0; i < loops; i++) {
 		Pthread_mutex_lock(&mutex); // c1
 		while (count == 0)             // c2
 			Pthread_cond_wait(&fill, &mutex); // c3
 		int tmp = get();            // c4
 		Pthread_cond_signal(&empty); // c5
 		Pthread_mutex_unlock(&mutex);// c6
		printf("%d\n", tmp);
	}
}

在上述代码中,生产者线程等待条件变量 empty,发信号给变量 fill。相应地,消费者线程等待 fill,发信号给 empty。这样做,从设计上避免了上述第二个问题:消费者再也不会唤醒消费者,生产者也不会唤醒生产者。

最终的生产者/消费者方案

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-22 20:46:35  更:2022-03-22 20:48:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:30:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码