背景
我对条件变量如何与共享数据的并发访问结合使用的理解有些困惑。以下是描述我当前问题的伪代码。
// Thread 1: Producer
void cakeMaker()
{
lock(some_lock);
while(number_of_cakes == MAX_CAKES)
wait(rack_has_space);
number_of_cakes++;
signal(rack_has_cakes);
unlock(some_lock);
}
// Thread 2: Consumer
void cakeEater()
{
lock(some_lock);
while(number_of_cakes == 0)
wait(rack_has_cakes);
number_of_cakes--;
signal(rack_has_space);
unlock(some_lock);
}
让我们考虑以下场景:number_of_cakes
为 0。因此,Thread 2
被阻止在wait(rack_has_cakes)
. When Thread 1
运行并增加number_of_cakes
到 1,它发出信号rack_has_cakes
。然而,Thread 2
之前醒来Thread 1
释放锁定some_lock
,导致它重新进入睡眠状态并错过信号。
我不清楚操作wait
and signal
。它们是否像一个拨动开关,当signal
被调用且为 0 时wait
成功了吗?有人可以解释幕后发生的事情吗?
Question
有人可以引导我逐步完成上述代码的一次迭代,重点关注期间发生的事件吗?signal
and wait
方法调用?
线程2在线程1调用unlock(some_lock)之前醒来,所以它继续
再次进入睡眠状态,信号已丢失。
不,事情不是这样的。我将使用 C++std::condition_variable
对于我的引用,但是 POSIX 线程以及互斥体和条件变量的大多数普通实现都以相同的方式工作。基本概念是相同的。
当线程 2 开始等待条件变量时,它已锁定互斥体。 wait() 操作解锁互斥体并等待条件变量原子地 http://en.cppreference.com/w/cpp/thread/condition_variable/wait:
原子地释放锁,阻塞当前正在执行的线程,并且
将其添加到等待 *this 的线程列表中。
该操作被认为是“原子的”;换句话说,不可分割。
然后,当条件变量发出信号时,线程重新锁定互斥体:
解锁时,无论什么原因,都会重新获取锁并等待
退出。
在另一个线程“调用解锁”之前,该线程不会“返回睡眠状态”。如果互斥量尚未解锁:当线程因条件变量的信号而唤醒时,线程将始终等待,直到再次成功锁定互斥量。这是无条件的。什么时候wait()
返回互斥锁仍处于锁定状态。然后,也只有到那时,wait()
函数返回。因此,事件的顺序是:
一个线程锁定互斥体,将某些计数器、变量或任何类型的互斥体保护数据设置为另一线程正在等待的状态。执行此操作后,线程向条件变量发出信号,然后在闲暇时解锁互斥体。
另一个线程已经在它之前锁定了互斥体wait()
s 位于条件变量上。之一wait()
的先决条件是互斥量必须在之前被锁定wait()
链接的条件变量。因此,wait() 操作“原子地”解锁互斥锁。也就是说,互斥体解锁时不存在实例,并且线程尚未等待条件变量。什么时候wait()
解锁互斥体,可以保证线程将等待,并且它将被唤醒。你可以把它带到银行。
一旦条件变量发出信号,wait()
荷兰国际集团线程does not从返回wait()
直到它可以重新锁定互斥锁。从条件变量接收到信号只是第一步,在最后一步中,线程必须再次锁定互斥锁。wait()
手术。当然,这只发生在信号线程解锁互斥体之后。
当线程收到条件变量的信号时,它will从返回wait()
。但不是立即,它必须等待,直到线程再次锁定互斥体,无论需要多长时间。它不会“回到睡眠状态”,而是等到再次锁定互斥锁,然后返回。您可以保证收到的条件变量信号将导致线程从wait()
,并且互斥体将被线程重新锁定。而且由于原始的解锁然后等待操作是原子的,因此您可以保证收到条件变量信号。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)