首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Boost.Thread在1.58中醒得太晚了

Boost.Thread在1.58中醒得太晚了
EN

Stack Overflow用户
提问于 2015-05-21 18:41:19
回答 3查看 1.3K关注 0票数 7

我有一个应用程序需要在某些窗口内工作(在本例中,这些窗口相隔30秒)。当时间不在窗口内时,计算到下一个窗口中间的时间,然后线程就会在这个时间段内休眠(使用boost::this_thread::sleep_for,以毫秒为单位)。

使用Boost 1.55,我能够在我的容忍度(+/-100 my )内以极高的可靠性击中窗口。在迁移到1.58的时候,我永远也打不到这些窗口。将boost::this_thread::sleep_for替换为std::this_thread::sleep_for解决了这个问题;但是,我需要boost::thread的可中断特性和boost::this_thread::sleep_for提供的中断点。

下面是一些说明问题的示例代码:

代码语言:javascript
运行
复制
#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位)上运行时的输出:

代码语言:javascript
运行
复制
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开始就引入了这个bug?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-05-23 16:13:28

我是对Boost.Thread做出上述改变的人。在1.58中的这一变化是在与Boost社区和微软进行了一段时间的磋商之后设计出来的,并导致移动设备上潜在的巨大电池寿命的改善。C++标准不保证任何时间等待实际上等待,或等待正确的时间段,或任何接近正确的时间段。因此,任何为假设时间等待工作或精确而编写的代码都是错误的。未来的Microsoft可能会对Boost.Thread进行类似的更改,因此STL的行为将与Boost.Thread相同。我可以补充一点,在任何非实时操作系统上,任何定时等待都是固有的不可预知的,任何触发时间都可能比请求晚得多。因此,社区认为这一变化有助于暴露STL的错误使用。

此更改允许Windows可选择地将计时器延迟一定的时间。实际上,它可能不会这样做,实际上,它只是试图延迟常规中断,作为Windows最新版本上的无痒内核设计的一部分。即使您指定的容忍数周,因为正确的截止日期总是发送到Windows,下一个系统中断发生在计时器到期后总是会触发定时器,因此任何计时器最多也不会迟到几秒钟。

此更改修复的一个错误是系统睡眠问题。以前的实现可能会被休眠的系统所迷惑,在这种情况下,定时等待永远不会被唤醒(好吧,在29天内,它们就会醒来)。这个实现正确地处理了系统休眠,使用系统休眠引起的使用Boost.Thread的随机代码挂起--希望现在已经过去了。

最后,我个人认为时间等待需要在STL中有一个硬度/柔软性的保证。不过,这是一个相当大的变化。即使实现了,除了在硬实时操作系统上,时间等待的硬度只能是最好的努力。这就是为什么它们一开始就被排除在C++标准之外的原因,因为在移动设备功耗被认为足以修改API之前,C++ 11就已经完成了。

尼尔

票数 7
EN

Stack Overflow用户

发布于 2015-05-21 22:34:36

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

在libs/线程/src/win32 32 /线程.src中,容忍度为睡眠时间的5%或32 ms,两者以较大者为准:

代码语言:javascript
运行
复制
// 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的睡眠时间如此不准确。

票数 1
EN

Stack Overflow用户

发布于 2016-04-15 09:49:19

如果需要sleep_for的中断性,我将使用这些代码作为解决方案:

代码语言:javascript
运行
复制
        ::Sleep(20);
        boost::this_thread::interruption_point();
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30381866

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档