condition_variable :: wait_for()如何处理虚假的唤醒?

时间:2015-08-07 06:43:10

标签: c++ multithreading c++11 condition-variable spurious-wakeup

各种平台允许

Spurious wakup。为了解决这个问题,我们在下面写出循环机制:

while(ContinueWaiting())
  cv.wait(lock);   // cv is a `std::conditional_variable` object

conditional_variable::wait_until()同样可以理解 但请看下面的例子:

const auto duration = Returns_10_seconds();
while(!Predicate())
  cv.wait_for(lock, duration);

想象一下,虚假的唤醒发生在1秒钟。尚未达到超时。
它会再等10秒吗?这将导致无限循环,我相信不应该发生。从源代码开始,内部wait_for()调用wait_until()

我想了解,wait_for()如何处理虚假的唤醒?

4 个答案:

答案 0 :(得分:5)

  

我想了解,wait_for()如何处理虚假的唤醒?

它没有。

此功能通常用于以下情况:如果您虚假醒来,无论如何都要做其他工作。如果你不是虚假地醒来,你想强迫一个虚假的"在duration过去之前醒来。这意味着,正如您所说明的那样,它通常不会在您显示的循环中使用。即超时和虚假唤醒被视为相同

现在您可能想知道,谓词版本的作用是什么,因为它意味着循环?

template <class Rep, class Period, class Predicate>
bool
wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time,
         Predicate pred);

指定它具有与以下相同的效果:

return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));

wait_until变体 区分虚假唤醒和超时。它是这样的循环:

while (!pred())
    if (wait_until(lock, abs_time) == cv_status::timeout)
        return pred();
return true;

答案 1 :(得分:2)

以下是关于虚假唤醒的标准:

  

30.5条件变量[thread.condition]

     

条件变量提供用于阻塞线程的同步原语,直到被其他人通知为止   满足某些条件或直到达到系统时间的线程。

     

...

     

10注意:用户有责任确保等待线程   不要错误地认为线程已经完成了   经历虚假的唤醒。

从措辞中可以清楚地看出,处理虚假唤醒的责任在于用户。

答案 2 :(得分:1)

<击>

    const auto duration = Returns_10_seconds();
    while(cv.wait_for(lock, duration) == std::cv_status::timeout);

<击>

这绝对是一件错误的事情,因此讨论如何在虚假唤醒的情况下修复它是没有意义的,因为它甚至在普通唤醒的情况下都被打破了,因为从等待返回后不再重新检查等待条件。

const auto duration = Returns_10_seconds();
while(!Predicate())
  cv.wait_for(lock, duration);

即使在编辑之后,答案也保持不变:你无法真正处理&#34;虚假的唤醒&#34;,因为你无法真正说出唤醒的原因 - 它可能会很好由于在超时到期之前调用condition_variable::notifyXXX,所以是完全合法的唤醒。

首先,请注意,您无法真正区分由condition_variable::notifyXXX调用引起的唤醒和由POSIX信号[1]引起的唤醒。 其次,即使POSIX信号不受关注,等待线程仍然必须重新检查条件,因为条件变量发出信号并且等待线程从条件等待返回之间条件可能会发生变化。

你真正需要做的是以特殊方式对待,不要在超时之前醒来,而是因为超时而唤醒 。这完全取决于首先出现超时的原因,即应用/问题域的具体情况。

[1]如果等待条件变量被信号中断,则在执行信号处理程序后,允许线程继续等待或返回

答案 3 :(得分:1)

检查自己打个电话

cv.wait_for()不处理虚假唤醒。

您可以通过将布尔标志通过引用传递给线程来处理虚假唤醒,并在cv.wait_until()不是超时时对其进行检查。

在这种情况下,当main()线程未设置terminatecv.wait_until()具有no_timeout时,这意味着未达到超时,但cv被通知(由系统通知),因此它是一个虚假电话。

bool terminate = false;
std::unique_lock<std::mutex> lock(mutex);
const auto time_point = std::chrono::system_clock::now() + std::chrono::seconds(10);
const std::cv_status status = cv.wait_until(lock, time_point);
if (status == std::cv_status::timeout) {
    std::cout << "timeout" << std::endl;
}
else { // no_timeout
    if (terminate) {
        std::cout << "terminate" << std::endl;
        break;
    }
    else {
        std::cout << "spurious" << std::endl;
    }
}

完整代码

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <chrono>

class Thread {
private:
    std::condition_variable cv;
    std::mutex mutex;
    bool terminate = false;
    std::thread thread;
public:
    Thread() {
        thread = std::thread([&]() {
            while (true) {
                std::unique_lock<std::mutex> lock(mutex);
                const auto time_point = std::chrono::system_clock::now() + std::chrono::seconds(10);
                const std::cv_status status = cv.wait_until(lock, time_point);
                if (status == std::cv_status::timeout) {
                    std::cout << "timeout" << std::endl;
                }
                else { // no_timeout
                    if (terminate) {
                        std::cout << "terminate" << std::endl;
                        break;
                    }
                    else {
                        std::cout << "spurious" << std::endl;
                    }
                }
            }
        });
    }
    virtual ~Thread() {
        {
            std::lock_guard<std::mutex> lock(mutex);
            terminate = true;
        }
        cv.notify_all();
        if (thread.joinable()) {
            thread.join();
        }
    }
};

void main() {
    {
        Thread thread;
        std::this_thread::sleep_for(std::chrono::seconds(15));
    }
    std::cin.get();
}

main()sleep_for()时的完整代码结果

5 seconds:
terminate

15 seconds:
timeout
terminate

15 seconds in Visual Studio Debug mode:
spurious
terminate