操作系统 — 浅析条件变量

浅析条件变量








Single UNIX Specification目前定义了条件变量的两个属性:进程共享属性和时钟属性. 条件变量的作用用于多线程之间关于共享数据状态变化的

信. 当一个动作需要另外一个动作完成时才能进行时. 也就是说:当一个线程的行为依赖于另外一个线程对共享数据状态的改变时,这时候就可

使用条件变量. 假如没有条件变量,举个最常见的消费者生产者模型.小明去银行去取钱,当他进入取款机的小房子后,发现这个取款机中的钱不

够他的要的钱,然 它离开了小房子,然后小明这个人性子比较急出去不到一秒,马上又扭头进去ATM机的小房子那里重新查询资源够不够它拿,然

后就可以看到一个 人跟 个傻子一样在ATM机那里出来进去,这个时候出现问题了! 如果这个银行还有别的人取钱,然后机子还不够,那么小明后面

会排好多人等它取 完钱,但是小明就是要跟ATM机较劲每隔一秒就查看一次. 这个时候他就浪费了资源,排在他身后的人可能有人取的钱ATM机里面

就够用了,但是它 没有机会去取钱. 因为小明一直在那里以极短的时间出来进去,别人根本就没有机会.


如果你觉得小明太智障了,生活中怎么可能会有这种人!! 但是我们把这个场景模仿到操作系统中,小明是一个优先级极高的线程,然后它访问一

段资源,在他进入的时候将该资源加锁,然后发现里面的资源不够自己使用然后再将资源解锁让别的线程使用. 但是小明的优先级实在太高了,然

后它又重复上面的操作,这种行为对于CPU来说是非常浪费时间效率的. 当然小明可以这样,发现资源不够使用的时候,sleep一段时间,然后再查

询,那么问题来了! 睡多久! 时间太长,实时性不好. 时间太短,浪费CPU时间效率.


现在是拥有条件变量的情况! 小明访问资源加上互斥量,然后发现资源不够自己访问,释放互斥量,然后去旁边等,等待工作人员告诉他你可以来

取钱了 ,ATM机里面的钱够你取了.  所以我们现在来认识条件变量吧! 既然它这么强大. 在使用条件变量之前,必须先对它进行初始化. 由

pthread_cond_t数据类型表示的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但如果条

件变量是动态分配的,则需要使用pthread_cond_init函数对它进行初始化! 在释放条件变量底层的内存空间之前,可以使用pthread_cond_destroy

函数对条件变量进行反初始化!!!!!

 #include <pthread.h>

int pthread_cond_destroy(pthread_cond_t*cond); 

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

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
除非需要创建一个具有非默认属性的条件变量,否则pthread_cond_init函数的attr参数可以设置为NULL. 接下来我们了解那个最核心的功能,

被别人唤醒等待方式!我们使用pthread_cond_wait等待条件变量变为真. 如果在给定的时间内条件不能满足,那么就会生成一个返回错误码变量.

 
  
 #include <pthread.h>

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

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
 
 
两个函数的返回值: 若成功返回0 否则 返回错误编号. 与前文无关,这是一个笔记(获取当前时间  gettimeofday(&now,NULL));

传递给pthread_cond_wait的互斥量对条件进行保护. 调用者把锁住的互斥量传给函数,函数然后自动把调用线程放到等待条件的线程列表上,对互

斥量解锁. 这就关闭了条件检查和线程进入休眠状态等待条件改变着两个操作之间的时间通道,这样线程就不会错过条件的任何变化. 

pthread_cond_wait返回之后,互斥量再次锁住. pthread_cond_timedwait函数的功能与pthread_cond_wait函数相似,只是多了一个超时值. 超时

值指定了我们愿意等待多长时间,他是通过tiomespec结构指定的. 需要指定原意等待多长时间,这个时间值是一个绝对数而不是一个相对数.

例如我们想等3分钟,那么就是将当前时间加上3分钟再转换成timespec结构. 可以使用clock_gettime函数获取timespec结构表示的当前时间.

如果超时到期时条件还是没有出现,pthread_cond_timewait将重新获取互斥量,然后返回错误ETIEDOUT. 从pthread_cond_wait或者

pthread_cond_timedwait调用成功返回时,线程需要重新计算条件,因为另一个线程可能已经在运行并改变了条件.

有两个函数可以用于通知线程条件已经满足. pthread_cond_signal函数至少能唤醒一个等待该条件的线程,而pthread_cond_broadcast函数则能唤

醒等待该条件的所有线程.

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);

int pthread_cond_signal(pthread_cond_t *cond);
在调用pthread_cond_signal或者pthread_cond_broadcast时,我们说这是在给线程或者条件发信号. 一定在改变条件状态之后再给线程发信号.

那我们来使用一下条件变量吧! 加入我们再练投篮一个人给你捡球,然后你接球投篮,那么当别人给你传球给你之前! 你不能够一直投篮吧?

(如果你练假动作除外),要不然会销毁你的体力,那么我们就写一个投篮传球使用条件变量的代码吧:

/*************************************************************************
	> File Name: pthread4.c
	> Author: ma6174
	> Mail: [email protected] 
	> Created Time: Mon 22 Jan 2018 04:34:26 AM PST
 ************************************************************************/

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

pthread_cond_t g_cond;

pthread_mutex_t g_lock;
  
void* ThreadEntry1(void* arg)
{
	while(1)
	{
		printf("传球->");
		pthread_cond_signal(&g_cond);
		usleep(678123);
	}
	return NULL;
}

void* ThreadEntry2(void* arg)
{
	while(1)
	{
		pthread_cond_wait(&g_cond,&g_lock);
		printf("投篮O\n");
		usleep(123456);
	}
	return NULL;
}

int main()
{
	pthread_cond_init(&g_cond,NULL);
	pthread_mutex_init(&g_lock,NULL);

	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,ThreadEntry1,NULL);
	pthread_create(&tid2,NULL,ThreadEntry2,NULL);
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	pthread_cond_destroy(&g_cond);
	pthread_mutex_destroy(&g_lock);
}

所以运行结果应该就是传球 投球. 这样就不会浪费资源了! 那么我们来看执行完毕的结果是什么? ? ? ? ?



这结果就是我们所预料的,看完这个博客我相信你对条件变量会有自己的一定的理解! 我们一定要熟悉条件变量的相关函数参数以及使用场景.

特别是pthread_cond_wait. 以及了解条件变量的

猜你喜欢

转载自blog.csdn.net/dawn_sf/article/details/79136164