簡體   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