【问题标题】:How would a thread acquire the lock after being awaken by a mesa semaphore?线程被台面信号量唤醒后如何获取锁?
【发布时间】:2020-07-02 05:14:24
【问题描述】:

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)应该等到锁被释放(至少他们是这么说的,它一直等到锁被释放)。

我的问题是:


1 - 这是如何工作的?

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

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

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


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

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

当生产者命中line 3consumer被唤醒后,就像拥有锁一样),锁将再次释放,另一个线程将能够获取锁,因此两个线程认为他们都拥有锁......

【问题讨论】:

    标签: multithreading synchronization locking semaphore


    【解决方案1】:

    线程会醒来,发现队列不为空,继续,命中第3行,等到锁空闲,获取,然后释放。

    如果队列已经非空,则永远不会调用wait,并且互斥锁在 2 到 3 之间的... 期间仍然是头部。如果队列为空,则调用wait,原子地释放互斥锁并等待,然后重新获取互斥锁。无论哪种方式,2 到 3 之间的 ... 都会在持有互斥体且队列非空的情况下执行。因此,您可以将代码从队列中取出。

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

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

    这取决于实现来做最有效的事情。它可能会尝试忙于等待很短的时间,但它可能只会使用内核函数使线程不再准备好运行。

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

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

    当生产者到达第 3 行时(消费者被唤醒并表现得像拥有锁一样),锁将再次被释放,另一个线程将能够获取锁,因此两个线程认为他们两者都拥有锁......

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

    【讨论】:

    • 感谢您的回答。如果consumer 持有锁直到它完成然后将其返回给producer,那将是hoare semaphore,对吗?另外,要成为mesa semaphore,我认为line 2 必须在生产者代码中的line 3 之后,对吧?
    【解决方案2】:

    首先consumer代码的cmets有错误,

    具体来说,在这个实现中,线程的休眠以及重新获取锁发生在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函数内部继续,退出函数后,将获得锁。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-05
      • 2011-06-24
      • 2015-09-16
      • 1970-01-01
      • 2021-01-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多