从技术上讲,std::chrono::system_clock::duration::max() 应该睡很长时间(比你或你的孙子孙女的寿命还要长)。标准强制执行。
但实际上,实现者仍在学习如何处理由 chrono 在不同精度的持续时间之间转换引起的溢出。所以bug很常见。
睡9'000h(一年多一点)可能更实用。这不可能导致溢出。对于您的应用程序来说,它肯定是“永远”的。
但是,请不要犹豫,向您的供应商发送错误报告,抱怨 std::chrono::system_clock::duration::max() 不起作用。这应该。让它正常工作很棘手。让它工作是不可移植的,所以要求你写一些包装来做是不合理的。
受到isanae 下方要求参考的出色评论的启发:
30.3.3 [thread.thread.this]/p7 描述了sleep_for 说:
效果:在rel_time指定的相对超时(30.2.4)内阻塞调用线程。
30.2.4 [thread.req.timing] 这是线程支持库中所有时序要求的规范,说:
2 实现在从超时返回时必然会有一些延迟。中断响应、函数返回和调度中的任何开销都会导致“实现质量”延迟,表示为持续时间Di。理想情况下,此延迟为零。此外,任何对处理器和内存资源的争用都会导致“管理质量”延迟,表示为持续时间Dm。延迟持续时间可能因超时而异,但在所有情况下,越短越好。
3 名称以_for 结尾的成员函数采用指定持续时间的参数。这些函数产生相对超时。实现应该使用一个稳定的时钟来测量这些函数的时间。330给定一个持续时间参数Dt,超时的实时持续时间是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> 的反面。前者找到一个转换为只划分的持续时间。后者会找到一个持续时间,该持续时间只会成倍增加。