提振.线程在1.58中醒来太晚了
Boost.Thread wakes up too late in 1.58
我有一个需要在某些窗口内工作的应用程序(在这种情况下,窗口之间的间隔为30秒)。当时间不在一个窗口内时,计算到下一个窗口中间的时间,并且线程休眠这段时间(以毫秒为单位,使用boost::this_thread::sleep_for
)。
使用Boost 1.55,我能够在我的容忍范围内(+/-100ms)以极高的可靠性击中窗口。在迁移到Boost 1.58后,我永远无法击中这些窗口。用std::this_thread::sleep_for
代替boost::this_thread::sleep_for
解决了这个问题;但是,我需要boost::thread
的可中断特性和boost::this_thread::sleep_for
提供的中断点。
#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#include <chrono>
#include <iostream>
#include <thread>
void boostThreadFunction ()
{
std::cout << "Starting Boost thread" << std::endl;
for (int i = 0; i < 10; ++i)
{
auto sleep_time = boost::chrono::milliseconds {29000 + 100 * i};
auto mark = std::chrono::steady_clock::now ();
boost::this_thread::sleep_for (sleep_time);
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now () - mark);
std::cout << "Boost thread:" << std::endl;
std::cout << "tSupposed to sleep for:t" << sleep_time.count ()
<< " ms" << std::endl;
std::cout << "tActually slept for:t" << duration.count ()
<< " ms" << std::endl << std::endl;
}
}
void stdThreadFunction ()
{
std::cout << "Starting Std thread" << std::endl;
for (int i = 0; i < 10; ++i)
{
auto sleep_time = std::chrono::milliseconds {29000 + 100 * i};
auto mark = std::chrono::steady_clock::now ();
std::this_thread::sleep_for (sleep_time);
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now () - mark);
std::cout << "Std thread:" << std::endl;
std::cout << "tSupposed to sleep for:t" << sleep_time.count ()
<< " ms" << std::endl;
std::cout << "tActually slept for:t" << duration.count ()
<< " ms" << std::endl << std::endl;
}
}
int main ()
{
boost::thread boost_thread (&boostThreadFunction);
std::this_thread::sleep_for (std::chrono::seconds (10));
std::thread std_thread (&stdThreadFunction);
boost_thread.join ();
std_thread.join ();
return 0;
}
以下是引用Boost 1.58作为包含目录并在我的工作站(Windows 7 64位)上运行时的输出:
Starting Boost thread
Starting Std thread
Boost thread:
Supposed to sleep for: 29000 ms
Actually slept for: 29690 ms
Std thread:
Supposed to sleep for: 29000 ms
Actually slept for: 29009 ms
Boost thread:
Supposed to sleep for: 29100 ms
Actually slept for: 29999 ms
Std thread:
Supposed to sleep for: 29100 ms
Actually slept for: 29111 ms
Boost thread:
Supposed to sleep for: 29200 ms
Actually slept for: 29990 ms
Std thread:
Supposed to sleep for: 29200 ms
Actually slept for: 29172 ms
Boost thread:
Supposed to sleep for: 29300 ms
Actually slept for: 30005 ms
Std thread:
Supposed to sleep for: 29300 ms
Actually slept for: 29339 ms
Boost thread:
Supposed to sleep for: 29400 ms
Actually slept for: 30003 ms
Std thread:
Supposed to sleep for: 29400 ms
Actually slept for: 29405 ms
Boost thread:
Supposed to sleep for: 29500 ms
Actually slept for: 29999 ms
Std thread:
Supposed to sleep for: 29500 ms
Actually slept for: 29472 ms
Boost thread:
Supposed to sleep for: 29600 ms
Actually slept for: 29999 ms
Std thread:
Supposed to sleep for: 29600 ms
Actually slept for: 29645 ms
Boost thread:
Supposed to sleep for: 29700 ms
Actually slept for: 29998 ms
Std thread:
Supposed to sleep for: 29700 ms
Actually slept for: 29706 ms
Boost thread:
Supposed to sleep for: 29800 ms
Actually slept for: 29998 ms
Std thread:
Supposed to sleep for: 29800 ms
Actually slept for: 29807 ms
Boost thread:
Supposed to sleep for: 29900 ms
Actually slept for: 30014 ms
Std thread:
Supposed to sleep for: 29900 ms
Actually slept for: 29915 ms
我希望std::thread
和boost::thread
的睡眠时间相同;然而,当要求睡眠29.1 - 29.9秒时,boost::thread
似乎想要睡眠约30秒。是我误用了boost::thread
接口,还是这是1.55以来引入的错误?
我是提交上述更改到Boost.Thread的人。1.58中的这一变化是在与Boost社区和微软进行了一段时间的磋商后设计出来的,它可能会极大地改善移动设备的电池寿命。c++标准不保证任何定时等待实际等待,或等待正确的周期,或任何接近正确的周期。因此,任何假设定时等待工作或准确的代码都是有bug的。未来的Microsoft STL可能会对Boost进行类似的更改。线程,因此STL行为将与Boost.Thread相同。我可能会补充说,在任何非实时操作系统上,任何时间等待都是不可预测的,可能会比请求的时间晚很多。因此,社区认为此更改有助于暴露STL的错误使用。
该更改允许Windows可选地延迟一定数量的计时器。它实际上可能不会这样做,实际上只是试图延迟常规中断,作为Windows最新版本的无麻烦内核设计的一部分。即使您指定了周的公差,因为正确的截止日期总是发送给Windows,计时器到期后发生的下一个系统中断总是会触发计时器,所以计时器最多不会延迟超过几秒钟。
这个修改修复的一个bug是系统睡眠问题。之前的实现可能会因为系统休眠而感到困惑,因为定时等待永远不会唤醒(好吧,29天后它们会唤醒)。这个实现使用Boost正确地处理了系统休眠和代码的随机挂起。由系统休眠引起的线程问题已经成为过去。
最后,我个人认为定时等待需要在STL中保证硬度/柔软度。然而,这是一个相当大的变化。即使实现了,除了在硬实时操作系统上,定时等待的困难也只能是最好的努力。这就是为什么它们一开始就被排除在c++标准之外,因为在移动设备功耗被认为重要到足以修改api之前,c++ 11就已经完成了。
尼尔
从Windows上的Boost 1.58开始,sleep_for()
利用SetWaitableTimerEx()
(而不是SetWaitableTimer()
)传递一个容忍时间来利用合并计时器。
在lib/thread/src/win32/thread.cpp中,公差为睡眠时间的5%或32ms,以较大者为准:
// Preferentially use coalescing timers for better power consumption and timer accuracy
if(!target_time.is_sentinel())
{
detail::timeout::remaining_time const time_left=target_time.remaining_milliseconds();
timer_handle=CreateWaitableTimer(NULL,false,NULL);
if(timer_handle!=0)
{
ULONG tolerable=32; // Empirical testing shows Windows ignores this when <= 26
if(time_left.milliseconds/20>tolerable) // 5%
tolerable=time_left.milliseconds/20;
LARGE_INTEGER due_time=get_due_time(target_time);
bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,&detail_::default_reason_context,tolerable)!=0;
if(set_time_succeeded)
{
timeout_index=handle_count;
handles[handle_count++]=timer_handle;
}
}
}
由于29.1秒的5%是1.455秒,这就解释了为什么使用boost::sleep_for
的睡眠时间是如此不准确。
如果我需要sleep_for:
的可中断性,我使用这段代码作为变通方法 ::Sleep(20);
boost::this_thread::interruption_point();
- 从不同线程使用int64的不同字节安全吗
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 在C++中使用cURL和多线程
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 在cuda线程之间共享大量常量数据
- 如何将元素添加到数组的线程安全函数?
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 异常属于C++中的线程还是进程
- C++中的线程安全删除
- C++使用params创建线程函数会导致转换错误
- 类与私有变量的其他类之间的线程安全性
- CoInitialize()在单独的线程上崩溃而不返回
- c++中的线程池
- 线程之间的布尔停止信号
- 为什么std::async使用同一个线程运行函数
- C 线程中的notify_one()醒来多个线程
- 线程没有从睡眠中醒来
- 提振.线程在1.58中醒来太晚了