c - wait函数究竟是如何作用于条件变量的

标签 c multithreading locking

背景

我对条件变量如何与共享数据的并发访问相关的工作方式的理解有点困惑。以下是说明我当前问题的伪代码。

// 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) 上。 线程 1 运行并将 number_of_cakes 加 1。然后它调用 signal(rack_has_cakes) - 这会唤醒 线程 2 ,不幸的是,Thread 2Thread 1 调用 unlock(some_lock) 之前就被唤醒了,所以它又回到了休眠状态,信号已经丢失了。

我对 waitsignal 的内部工作原理很困惑?他们到底发生了什么?它们是否像一些 bool 标志,当我们调用信号时自动设置为 1,并在等待成功时再次设置为 0?这是怎么回事?

问题

谁能带我逐步完成上述代码的一次迭代,并着重强调信号和等待调用期间发生的事件?

最佳答案

thread 2 wakes up before Thread 1 calls unlock(some_lock), so it goes back to sleep again and the signal has been missed.

不,这不是它的工作原理。我将使用 C++ std::condition_variable 作为我的引用,但是 POSIX 线程以及互斥量和条件变量的大多数常规实现以相同的方式工作。基本概念是相同的。

当线程 2 开始等待条件变量时,它锁定了互斥锁。 wait() 操作解锁互斥量和 waits on the condition variable atomically :

Atomically releases lock, blocks the current executing thread, and adds it to the list of threads waiting on *this.

这个操作被认为是“原子的”;换句话说,不可分割。

然后,当条件变量发出信号时,线程重新锁定互斥量:

When unblocked, regardless of the reason, lock is reacquired and wait exits.

在另一个线程“调用解锁”之前,该线程不会“回到 sleep 状态”。如果互斥量还没有被解锁:当线程被条件变量通知唤醒时,线程将一直等待,直到它再次成功锁定互斥量。这是无条件的。当 wait() 返回时,互斥量仍处于锁定状态。然后,也只有到那时,wait() 函数才会返回。所以,事件的顺序是:

  1. 一个线程锁定了互斥体,将一些计数器、变量或任何类型的互斥体保护数据设置为另一个线程正在等待的状态。这样做之后,线程向条件变量发出信号,然后在闲暇时解锁互斥体。

  2. 另一个线程在条件变量上 wait() 之前锁定了互斥量。 wait() 的先决条件之一是互斥量必须在 wait() 链接条件变量之前锁定。因此, wait() 操作“原子地”解锁了互斥体。也就是说,当互斥量解锁时没有实例,并且线程还没有等待条件变量。当 wait() 解锁互斥量时,您可以保证线程会等待,并且会被唤醒。你可以把它带到银行。

  3. 一旦条件变量发出信号,wait()ing 线程不会wait()返回,直到它可以重新锁定互斥量。收到来自条件变量的信号只是第一步,在 wait() 操作的最后一步中,必须由线程再次锁定互斥锁。当然,这只会在信号线程解锁互斥量之后发生。

当线程收到条件变量的信号时,它将wait() 返回。但不是立即,它必须等到线程再次锁定互斥量,无论需要多长时间。它不会“回到 sleep 状态”,而是等到互斥量再次被锁定,然后返回。您可以保证接收到的条件变量信号将导致线程从 wait() 返回,并且线程将重新锁定互斥体。并且因为最初的解锁然后等待操作是原子的,所以您可以保证接收到条件变量信号。

关于c - wait函数究竟是如何作用于条件变量的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47804723/

相关文章:

c++ - 了解 sigwait() 及其如何设置参数

c - 头文件中的数组声明

mysql - 使用 SELECT .. FOR UPDATE LIMIT 1 时应锁定多少行

c# - ReaderWriterLockSlim 扩展方法性能

java - 如果可以通过任何其他方法访问对象,那么在同步块(synchronized block)中锁定对象有什么用呢?

c++ - 从另一个进程中的内存执行一个进程?

C IEEE-Floats inf 等于 inf

java - 关闭套接字连接

c++ - 当主应用程序处于 while 循环时,boost 线程未运行

c# - 在 C# 中处理跨线程事件的最佳方法是什么