c – 我可以传递给std :: thread :: sleep_for()和sleep_until()的最大值是多少?

This question永远在睡觉有一个答案,提到这个:

std::this_thread::sleep_until(
    std::chrono::time_point<std::chrono::system_clock>::max());

还有这个:

std::this_thread::sleep_for(
    std::chrono::system_clock::durat‌​ion::max());

在Visual C 2017 RC上运行此代码实际上根本不会睡眠.我还没有检查出sleep_until()的情况,所以我不确定那里发生了什么.

在sleep_for()情况下,给定的持续时间似乎通过将其添加到system_clock :: now()转换为绝对时间,然后将其转发到sleep_until().问题是加法溢出,过去给了时间.

看一下30.3.2中的C 17草案,sleep_until()和sleep_for()似乎都没有提到限制.时序规范(30.2.4)中没有任何相关内容.至于duration :: max(),它在duration_values(20.17.4.3)中描述为:“返回的值应该大于零()”,这根本没有用.

老实说,我很惊讶看到sleep_for()因system_clock :: duration :: max()而失败,因为它是一个对我来说非常有意义的构造.

我可以传递给具有明确定义行为的函数的最高值是多少?

解决方法:

从技术上讲,std :: chrono :: system_clock :: durat ion :: max()应该睡很长时间(比你或你的孙子孙女还活着的时间长).标准强制执行.

但实际上,实现者仍然在学习如何处理在不同精度的持续时间内由计时转换引起的溢出.所以bug很常见.

睡觉9’000h(一年多一点)可能更实际.这不会导致溢出.对于您的应用来说,它肯定是“永远”的.

但是,请不要犹豫向您的供应商发送错误报告抱怨std :: chrono :: system_clock :: durat ion :: max()不起作用.这应该.让它正常工作真的很棘手.使它工作是不可移植的,所以要求你写一些包装器来做它是不合理的.

isanae的优秀评论激发下面的要求参考:

30.3.3描述sleep_for的[thread.thread.this] / p7说:

Effects: Blocks the calling thread for the relative timeout (30.2.4) specified by rel_time.

30.2.4 [thread.req.timing]是线程支持库中所有时序要求的规范,说:

2 Implementations necessarily have some delay in returning from a timeout. Any overhead in interrupt response, function return, and scheduling induces a “quality of implementation” delay, expressed as duration Di. Ideally, this delay would be zero. Further, any contention for processor and memory resources induces a “quality of management” delay, expressed as duration Dm. The delay durations may vary from timeout to timeout, but in all cases shorter is better.

3 The member functions whose names end in _for take an argument that specifies a duration. These functions produce relative timeouts. Implementations should use a steady clock to measure time for these functions.330 Given a duration argument Dt, the real-time duration of the timeout is Dt+ Di+ Dm.

好的,所以现在我很开心,因为我们不是在讨论成员函数.我们讨论的是命名空间范围函数.这是一个缺陷.随意到submit one.

但该规范没有提供溢出的优雅.规范(几乎)清楚地表明,实施不能在指定的延迟之后返回.关于多少之后,它是模糊不清的,但是之前它无法回归.

如果你“捣乱”STL并且他不合作,那就把他推荐给我,我们会解决它. :-)也许有一个我没有看到的标准错误,应该修复.如果是这样,我可以帮助您针对标准而不是针对VS提交错误.或者VS可能已经解决了这个问题,并且该修复程序可用于升级.

如果这是VS中的错误,请让STL知道我非常乐意协助修复它.在不同平台上解决此问题有不同的权衡.

目前,我不能发誓我自己的实现(libc)中没有这个类的错误.所以这里没有高马.对于std :: lib来说,这是一个很难实现的难点.

更新

我查看了libc sleep_for和sleep_until. sleep_for通过“长时间”睡眠来正确处理溢出(操作系统可以处理). sleep_until有溢出错误.

这是一个非常轻微测试的固定sleep_until:

template <class _Clock, class _Duration>
void
sleep_until(const chrono::time_point<_Clock, _Duration>& __t)
{
    using namespace chrono;
    using __ldsec = duration<long double>;
    _LIBCPP_CONSTEXPR time_point<_Clock, __ldsec> _Max =
                          time_point<_Clock, nanoseconds>::max();
    time_point<_Clock, nanoseconds> __ns;
    if (__t < _Max)
    {
        __ns = time_point_cast<nanoseconds>(__t);
        if (__ns < __t)
            __ns += nanoseconds{1};
    }
    else
        __ns = time_point<_Clock, nanoseconds>::max();
    mutex __mut;
    condition_variable __cv;
    unique_lock<mutex> __lk(__mut);
    while (_Clock::now() < __ns)
        __cv.wait_until(__lk, __ns);
}

基本策略是使用long double表示进行溢出检查,该表示不仅具有非常大的最大可表示值,而且还使用饱和算法(具有无穷大).如果输入值太大而无法操作系统处理,请将其截断为操作系统可以处理的内容.

在某些平台上,可能不希望采用浮点运算.有人可能会使用__int128_t.或者在进行比较之前,有一个更复杂的技巧转换为输入的“最小公倍数”和本机持续时间.该转换只涉及除法(不是乘法),因此不能溢出.但是,它并不总能给出几乎相等的两个值的准确答案.但它应该适用于这个用例.

对于那些对后者(lcm)策略感兴趣的人,以下是如何计算该类型:

namespace detail
{

template <class Duration0, class ...Durations>
struct lcm_type;

template <class Duration>
struct lcm_type<Duration>
{
    using type = Duration;
};

template <class Duration1, class Duration2>
struct lcm_type<Duration1, Duration2>
{
    template <class D>
    using invert = std::chrono::duration
                   <
                       typename D::rep,
                       std::ratio_divide<std::ratio<1>, typename D::period>
                   >;

    using type = invert<typename std::common_type<invert<Duration1>,
                                                  invert<Duration2>>::type>;
};

template <class Duration0, class Duration1, class Duration2, class ...Durations>
struct lcm_type<Duration0, Duration1, Duration2, Durations...>
{
    using type = typename lcm_type<
                     typename lcm_type<Duration0, Duration1>::type,
                     Duration2, Durations...>::type;
};

}  // namespace detail

可以想到lcm_type< duration1,duration2>与common_type< duration1,duration2>相反.前者找到转换为仅分割的持续时间.后者找到转换为仅相乘的持续时间.

上一篇:C++ 计算程序的运行时间


下一篇:C++获取时间戳(Linux)