当给定std :: chrono :: duration :: max时,std :: condition_variable :: wait_for会立即退出

时间:2014-12-31 22:17:48

标签: c++11 condition-variable mingw-w64 chrono

我有std::queue的包装器,使用C ++ 11语义来允许并发访问。 std::queuestd::mutex保护。当某个项目被推送到队列时,会通过调用std::condition_variable通知notify_one

有两种方法可以从队列中弹出一个项目。一个方法将无限期地阻塞,直到使用std::condition_variable::wait()将项目推入队列。第二个将阻止std::chrono::duration单位使用std::condition_variable::wait_for()

给出的时间
template <typename T> template <typename Rep, typename Period>
void ConcurrentQueue<T>::Pop(T &item, std::chrono::duration<Rep, Period> waitTime)
{
    std::cv_status cvStatus = std::cv_status::no_timeout;
    std::unique_lock<std::mutex> lock(m_queueMutex);

    while (m_queue.empty() && (cvStatus == std::cv_status::no_timeout))
    {
        cvStatus = m_pushCondition.wait_for(lock, waitTime);
    }

    if (cvStatus == std::cv_status::no_timeout)
    {
        item = std::move(m_queue.front());
        m_queue.pop();
    }
}

当我在空队列中调用这样的方法时:

ConcurrentQueue<int> intQueue;

int value = 0;
std::chrono::seconds waitTime(12);

intQueue.Pop(value, waitTime);

然后12秒后,对Pop()的调用将退出。但是如果将waitTime设置为std::chrono::seconds::max(),则对Pop()的调用将立即退出。毫秒:: max()和小时:: max()也是如此。但是,days :: max()按预期工作(不立即退出)。

是什么导致秒:: max()立即退出?

这是用mingw64编译的:

g++ --version

g++ (rev5, Built by MinGW-W64 project) 4.8.1
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

2 个答案:

答案 0 :(得分:4)

首先,定时等待应该是wait_until(lock, std::chrono::steady_clock::now() + waitTime);,而不是wait_for,因为循环现在只需重复等待多次,直到最后条件(m_queue.empty())变为真。重复也可能是由虚假的唤醒引起的。

使用谓词等待方法修复 部分代码:

template <typename Rep, typename Period>
bool pop(std::chrono::duration<Rep, Period> waitTime, int& popped)
{
    std::unique_lock<std::mutex> lock(m_queueMutex);

    if (m_pushCondition.wait_for(lock, waitTime, [] { return !m_queue.empty(); }))
    {
        popped = m_queue.back();
        m_queue.pop_back();
        return true;
    } else
    {
        return false;
    }
}

我的实施至少seconds::max()会产生0x7fffffffffffffff

§30.5.1ad26州:

  

效果:好像

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

否则

auto time = steady_clock::now() + seconds::max();
std::cout << std::dec << duration_cast<seconds>(time.time_since_epoch()).count() << "\n";

在我的系统上,打印

265521

使用date --date='@265521' --rfc-822告诉我那是Sun, 04 Jan 1970 02:45:21 +0100

GCC和Clang有一个包装错误,见下文


测试

<强> Live On Coliru

#include <thread>
#include <condition_variable>
#include <iostream>
#include <deque>
#include <chrono>
#include <iomanip>

std::mutex m_queueMutex;
std::condition_variable m_pushCondition;

std::deque<int> m_queue;

template <typename Rep, typename Period>
bool pop(std::chrono::duration<Rep, Period> waitTime, int& popped)
{
    std::unique_lock<std::mutex> lock(m_queueMutex);

    if (m_pushCondition.wait_for(lock, waitTime, [] { return !m_queue.empty(); }))
    {
        popped = m_queue.back();
        m_queue.pop_back();
        return true;
    } else
    {
        return false;
    }
}

int main()
{
    int data;
    using namespace std::chrono;

    pop(seconds(2)    , data);

    std::cout << std::hex << std::showbase << seconds::max().count() << "\n";
    auto time = steady_clock::now() + seconds::max();
    std::cout << std::dec << duration_cast<seconds>(time.time_since_epoch()).count() << "\n";
    pop(seconds::max(), data);
}

答案 1 :(得分:0)

出现问题的原因是该参数in the description for rel_time令人讨厌:

  

请注意,rel_time必须足够小,以免在添加到std :: chrono :: steady_clock :: now()时溢出。

因此,当您执行m_pushCondition.wait_for(lock, std::chrono::seconds::max());时,参数在函数内部溢出。实际上,如果启用undefined消毒剂,(例如GCC和Clang的-fsanitize=undefined选项),然后运行该应用程序,则可能会看到以下运行时警告:

/usr/include/c++/9.1.0/chrono:456:34: runtime error: signed integer overflow: 473954758945968 + 9223372036854775807 cannot be represented in type 'long int'

值得一提的是,由于某种原因,我没有针对正在处理的实际应用发出此警告,可能是消毒剂错误。无论如何。

那么您可以做什么。首先:不要尝试通过简单地使用wait_for()重载与谓词来解决此问题,因为这会使您的CPU内核烧坏。第二:减去max() - now()似乎不起作用,因为它会更改类型。

一种解决方法是有条件地使用condition_variable::wait()condition_variable::wait_for()

另一种可能是只声明大时间跨度,然后使用它。例如:

// This is a replacement to chrono::seconds::max(). The latter doesn't work with
// `wait_for` call because its `rel_time` parameter description has the following
// sentence: "Note that rel_time must be small enough not to overflow when added to
// std::chrono::steady_clock::now()".
const chrono::seconds many_hours = 99h;

// …[snip]…
    m_pushCondition.wait_for(lock, many_hours);
// …[snip]…

您可能可以容忍一次99小时的“虚假”唤醒:)