繁体   English   中英

Boost.Thread在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提供的中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::threadboost::thread能够在相同的时间内休眠; 然而,当被要求睡眠29.1秒至29.9秒时, boost::thread似乎想要睡眠约30秒。 我是否滥用了boost::thread接口,或者这是自1.55以来引入的错误?

我是将上述更改提交给Boost.Thread的人。 1.58的这一变化是在与Boost社区和微软进行了一段时间的协商之后设计的,并且导致移动设备上的电池寿命大大改善。 C ++标准不保证任何定时等待实际等待,或等待正确的时间段,或任何接近正确的时间段。 因此,为了假定定时等待工作或准确而编写的任何代码都是错误的。 未来的Microsoft STL可能会对Boost.Thread进行类似的更改,因此STL行为与Boost.Thread相同。 我可能会在任何非实时操作系统上添加它,任何定时等待本质上都是不可预测的,任何时候都可能比请求时间大得多。 因此,社区认为这种变化有助于暴露STL的错误使用。

此更改允许Windows可选地延迟一定量的计时器。 它可能实际上并没有这样做,实际上只是尝试延迟常规中断,作为最新版Windows上无滴答内核设计的一部分。 即使您指定了几周的容差,因为正确的截止日期始终发送到Windows,在计时器到期后发生的下一个系统中断将始终触发计时器,因此没有计时器最多会延迟超过几秒钟。

这种变化导致的一个错误就是系统睡眠问题。 以前的实现可能会被系统睡眠混淆,因为定时等待永远不会被唤醒(好吧,他们会在29天内)。 这个实现正确地处理了系统休眠,并且使用由系统休眠引起的Boost.Thread的代码随机挂起现在已成为过去。

最后,我个人认为定时等待需要STL中的硬度/柔软度保证。 然而,这是一个非常大的变化。 即使实施,除了在硬实时操作系统上,定时等待的硬度只能是最好的努力。 这就是为什么他们首先被排除在C ++标准之外,因为在移动设备功耗被认为足以修改API之前,C ++ 11已经完成。

尼尔

从Windows上的Boost 1.58开始, sleep_for()利用SetWaitableTimerEx() (而不是SetWaitableTimer() )传递容差时间以利用合并计时器。

在libs / thread / src / win32 / thread.cpp中,容差是睡眠时间的5%或32 ms,以较大者为准:

// 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();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM