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::duration::max());
在Visual C ++ 2017 RC上运行此代码实际上根本不会睡觉。我还没有查看sleep_until()
案例,所以我不确定那里发生了什么。
在sleep_for()
案例中,给定的duration
似乎会通过将其添加到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)中描述为:&#34;返回的值应大于zero()
&#34 ;,这根本没有帮助。
老实说,我很惊讶sleep_for()
system_clock::duration::max()
失败,因为它是一个对我来说非常有意义的构造。
我可以传递给具有明确定义行为的那些函数的最高值是什么?
答案 0 :(得分:3)
从技术上讲,std::chrono::system_clock::duration::max()
应该睡很长时间(比你或你的孙子孙女还活着的时间长)。标准强制执行。
但实际上,实现者仍在学习如何处理不同精度持续时间内chrono
次转换引起的溢出。所以错误很常见。
睡觉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实现从超时返回时必然有一些延迟。中断响应,函数返回和调度中的任何开销都会导致“实现质量”延迟,表示为持续时间
D
i 。理想情况下,此延迟将为零。此外,对处理器和存储器资源的任何争用都会引起“管理质量”延迟,表示为持续时间D
m 。延迟持续时间可能因超时而异,但在所有情况下,更短的更好。3名称以
_for
结尾的成员函数采用指定持续时间的参数。这些函数产生相对超时。实现应使用稳定时钟来测量这些函数的时间。 330 给定持续时间参数D
t ,超时的实时持续时间为{{ 1}} <子>吨子>D
<子> I 子>+ D
<子>米子>+ D
。
好的,现在我很开心,因为我们不是在讨论成员函数。我们讨论的是命名空间范围函数。这是一个缺陷。随意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>
相反。前者找到转换为仅分割的持续时间。后者找到转换为仅相乘的持续时间。
答案 1 :(得分:2)
我与Visual C ++标准库开发人员之一Billy O&#Neal和libc ++的主要作者Howard Hinnant进行过讨论。我的结论是线程库中的_for
和_until
系列将以未指定的方式溢出,您不应该尝试将大的值传递给它们。我不清楚标准是否在该主题上不明确。
所有定时函数 1 采用duration
或time_point
。两者都由其基础类型(表示)和比率(期间)定义。这段时间也可以被视为&#34;单位&#34;,例如秒或纳秒。
有两个可能发生溢出的主要地方:
在这种情况下可以避免溢出,就像霍华德在他的回答中提到的那样,但是实施者仍在学习如何处理不同精度持续时间内chrono
转换引起的溢出。 。
Visual C ++ 2017通过将给定的持续时间添加到返回的当前时间,以sleep_for()
实现sleep_until()
。
system_clock::now()
。如果持续时间太长,则会溢出。其他库,例如libstdc ++,似乎没有这个问题。
一旦你足够深入,你就必须与你正在进行实际工作的平台进行互动。这是它变得混乱的地方。
例如,在libstdc ++上,对sleep_for()
的调用最终在nanosleep()
,timespec
。这是它的简化版本:
auto s = duration_cast<seconds>(time);
auto ns = duration_cast<nanoseconds>(time - s);
timespec ts = { s.count(), ns.count() };
nanosleep(&ts, &ts);
很容易溢出这个:你只需要传递一个超过LLONG_MAX秒的时间:
std::this_thread::sleep_for(hours::max());
这会使duration_cast
溢出到seconds
并将ts.tv_sec
设置为-3600,因为nanosleep()
在负值上失败,所以它根本不会睡觉。 sleep_until()
尝试在循环中调用nanosleep()
会变得更好,但它会一直失败,因此在等待期间需要100%的处理器。
Visual C ++ 2017库中也发生了同样的事情。忽略sleep_for()
中的溢出,因为它将持续时间添加到当前时间,它最终调用Sleep
,它以毫秒为单位取无符号的32位值。
即使它调用了更像NtWaitForSingleObject()
(它可能在将来)的更灵活的东西,它仍然只是一个带符号的64位值,以100纳秒为增量并且仍然可以溢出。
我个人认为<chrono>
库中的溢出是一个错误,例如Visual C ++在sleep_for()
方面的sleep_until()
实现。我认为,在调用特定于平台的函数之前,无论你给出什么样的价值,都应该在最终转换之前不受影响。
一旦你到达那里,如果平台在你要求的时间内不支持睡眠,那么就没有真正的解决方案。由于<chrono>
被禁止抛出异常,我接受而不是溢出是一种可能性。虽然这会变成未定义的行为,但我希望实现在处理溢出时会更加小心,例如libstdc ++处理EINVAL和在紧密循环中旋转的各种失败。
我引用了我从Billy O&#;; Neal收到的电子邮件中的一些内容,因为它们增加了标准库开发人员的观点:
你是说这个:
this_thread::sleep_for(system_clock::duration::max());
是标准的未定义行为吗?
据我所知,是的。它是一种灰色区域 - 没有为这些函数指定最大允许范围,但考虑到它们接受任意
time_point
/duration
的性质,这可能得到某些用户的支持 - 提供标准库不知道的bignum类型,基本上强制转换为某些基础time_point
/duration
类型。<chrono>
的设计将溢出作为非目标处理(例如,请参阅duration_cast
,它完全禁止实施&#34;如同无穷大&#34;和类似)。< / p>标准[...]没有给我们任何方式来报告在这里转换失败 - 行为实际上是未定义的。我们明确被禁止抛出异常,我们无法推断如果您超过
LLONG_MAX
会发生什么,因此我们唯一可能的回答是&#34;就像无限&#34;或直接转到std::terminate()
,不要过去,不要收200美元。libstdc ++和libc ++是
system_clock
实际映射到平台所理解的东西的目标平台,其中Unix时间戳是土地的法则。我们没有针对这样的平台,并且有义务映射到/来自&#34;DWORD
毫秒&#34;和/或FILETIME
。关于我唯一能想到的可能是这个事情的合理用例,就是要有某种哨兵价值,这意味着&#34; infinity,&#34;但是如果我们想去那里,标准应该引入一个命名常量并描述其行为。
我宁愿解决你的直接问题(希望时间值成为无限的哨兵),而不是试图强制进行溢出检查。当您不了解所涉及的类型时,溢出检查可能会变得非常昂贵(复杂性和运行时间),但检查魔术常量(例如
chrono::duration<rep, period>::max()
或chrono::time_point<clock, duration>::max()
)应该是便宜。
看起来未来的更新(ABI不兼容)会对<thread>
进行重大更改,因此它不再在sleep_for()
中溢出,但它仍然受到Windows API的限制支持。像NtWaitForSingleObject()
这样的东西确实支持64位值,但签名,因为它支持相对(负)和绝对(正)时间。
1 &#34;定时函数&#34;,我的意思是 30.2.4 [thread.req.timing] 适用的任何函数,例如this_thread::sleep_for()
和this_thread::sleep_until()
,以及timed_mutex
,recursive_timed_mutex
,condition_variable
等内容