并发编程中条件变量(condition variables)实现原理

0 介绍

以前只是会使用Linux下的条件变量pthread_cond_t,知道它的作用是配合互斥锁解决并发编程的同步问题,没有思考过它的实现原理,今天学习了清华大学陈渝老师的操作系统网课,总算是明白了,特此记录。

1 实现

条件变量是一种等待机制,每一个条件变量对应一个等待原因与等待队列。一般对于条件变量会有两种操作:

  1. wait操作 : 将自己阻塞在等待队列里,唤醒一个等待者或者开放锁的互斥访问
  2. singal 操作 : 唤醒一个等待的线程(等待队列为空的话什么也不做)

下面看看它的伪码描述,两个变量一个描述在等待队列的线程数,一个就是等待队列:

class Condition
{
	int numWaiting = 0; //在队列中等待的线程数
	WaitQueue q; //等待队列
};

对于wait操作来说,首先给numWaiting加1,然后将当前线程加入等待队列里,然后释放开锁,schedule将放开cpu给其他线程,回来之后再次尝试获取锁。

Condition::Wait(lock)
{
	++numWaiting;
	Add this thread to WaitQueue q;
	release(lock);
	schedule(); //调度机制
	acquire(lock);
}

对于singal操作来说,则如果等待队列中有线程的话,将它取出唤醒,numWaiting减一即可。

Condition::Singal()
{
	if(numWaiting > 0)
	{
		remove t from q;
		wakeup(q);
		--numWaiting;
	}
}

2 应用

懂了这些实现后,看看如何利用前面的知识解决经典的生产者消费者问题。我们假定有多个生产者和多个消费者,生产者往buffer中加入产品,消费者从buffer中拿走产品。buffer的大小有上限,设为n,也就是说当buffer中的产品为n时生产者不能继续在buffer中添加商品,为0时消费者不能继续在buffer中拿走商品。
不难知道我们需要一个锁Lock,两个条件变量notEmpty和notFull分别用于指示消费者和生产者,count是目前buffer中的产品数。

class BoundedQueue
{
	Lock lock;
	count = 0;
	Condition notEmpty,notFull;
};

对于生产者来说,其执行的操作如下:首先活动锁之后,检查buffer是否满了,如果满了,则执行wait。添加好商品后,执行notEmpty的singal,唤醒一个消费者线程消费。

BoundedQueue::deposit
{
	acquire(lock);
	while(count == n)
		notFull.wait();
	add c to the buffer;
	++count;
	notEmpty.singal();
	release(lock);
}

对于消费者来说,其操作和生产者差不多:

BoundedQueue::remove()
{
	acquire(lock);
	while(count == n)
		notEmpty.wait();
	remove c from buffer;
	--count;
	notFull.Singal();
	release(lock);
}

猜你喜欢

转载自blog.csdn.net/MoonWisher_liang/article/details/110689470