线程被台面信号量唤醒后如何获取锁?

How would a thread acquire the lock after being awaken by a mesa semaphore?

this video中,有一个信号量的伪代码,用于解决生产者-消费者问题。生产者将物品放入队列中,消费者从队列中取出物品。代码如下所示:

拥有即将唤醒一个线程的锁的线程:

// producer

lock.acquire()     // 1
...
dataready.wakeOneThread() // 2 
... 
lock.release();    // 3

即将被唤醒的线程:

// consumer

lock.acquire()              // 1
...
while (queue.isEmpty()) {   // <------ program continues from here
    dataready.wait(lock);   // 2
}
...
lock.release();             // 3

函数wakeOneThread释放锁,将一个线程放入就绪队列,再次获取锁,在line 3释放锁(我可能错了)。将一个线程放入就绪队列后,如果producer仍在获取锁时线程被唤醒,则该线程(consumer)应该等待直到释放锁(at至少他们是这么说的,它会等到锁被释放。

我的问题是:


1 - 这是如何工作的?

线程会被唤醒,发现队列不为空,继续,命中line 3,等待锁空闲,获取,然后释放。

是否正在忙着等待释放锁?如果没有,那么,既然没有人会再次唤醒它,它怎么知道锁被释放了呢?

我不确定是不是这样,因为忙等待效率不高...


2 - 假设它正忙于等待,第二个问题,consumer 将继续并修改值,就好像它有锁一样。我相信锁应该在它开始的那一刻就被获取 运行...

我不确定是否是这种情况(线程在启动时获取锁),因为代码中没有指示会发生这种情况。

当producer命中line 3时(consumer被唤醒后,就好像拥有锁一样),锁会再次释放,另一个线程就可以获取了锁,因此两个线程认为他们都拥有锁...

The thread will wake up, find that the queue is not empty, continues, hits the line 3, waits until the lock is free, acquires it, then releases it.

如果队列已经非空,则永远不会调用 wait 并且互斥量在 2 和 3 之间的 ... 期间仍然是头部。如果队列为空,则 wait 被调用,自动释放互斥量并等待,然后重新获取互斥量。无论哪种方式,2 和 3 之间的 ... 都会在持有互斥锁且队列非空的情况下执行。所以你会把代码放在那里从队列中取出一个项目。

Is it busy waiting for the lock to be released? If not, then, since no one will wake it up again, how would it know that the lock is released?

I'm not sure if that's the case since busy waiting is not so efficient...

这取决于实现,以执行最有效的操作。它可能会尝试忙等待非常短的时间,但它可能只会使用内核函数使线程不再就绪-运行.

2 - Assuming that it's busy waiting, second problem, the consumer will continue and modify values as if it had the lock. I believe the lock should be acquired the moment it starts running...

消费者将在 ... 部分进行修改。那是在步骤 3 中释放锁之前。所以这是在持有锁的情况下完成的。

When the producer hits line 3 (after the consumer is awaken up and acts as if it owns the lock), the lock will be released again, and another thread would be able to acquire the lock, thus two threads thinking that they both own the lock...

生产者不拥有第 3 行之后的锁,也不需要它。第 2 步之前的 ... 表示将项目放入队列(尽管它可以在第 2 步之后的 ... 中完成。为 1 和 2 之间的所有代码保持锁定。

首先,consumer代码的注释有误,

具体来说,在这个实现中,线程休眠以及重新获取锁发生在 wait 函数内。

wait 函数看起来像这样:

... wait(SpinLock* lock)
{
    ...
    release_lock(lock);
    sleep()                 // sleeps and puts the thread in the wait channel.
    acquire_lock(lock)      // <------ program continues from here
    ...
}

我假设程序将从行 while (queue.isEmpty()) { 继续,但这是错误的。

程序实际上会从wait函数内部继续执行,退出函数后会获取锁。

这里使用了自旋锁,被唤醒的线程会忙等待直到锁被释放。这应该不会花费太多时间,因为自旋锁只会在生产者释放锁之前一直处于忙碌状态,这可能就在唤醒线程之后(生产者函数甚至可能在线程被唤醒之前完成,在这种情况下会有根本不用忙着等待)。