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

自己最初想的方向错了,从条件变量中需要加什么机制来考虑,而忽视了操作系统本身。

这里需要从操作系统侧考虑如何实现条件变量。操作系统是主动调用者,而条件变量其实是操作系统预留出的接口。

因而这里主要是去考虑记录谁在等待、记录谁要唤醒、如何唤醒的问题。


0 介绍


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

1 实现


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

wait操作 : 将自己阻塞在等待队列里,唤醒一个等待者或者开放锁的互斥访问
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);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值