1. 程式人生 > >boost::this_thread::sleep_for()死鎖

boost::this_thread::sleep_for()死鎖

boost::this_thread::sleep_for()會死鎖

(金慶的專欄)

發現睡眠1ms很容易死鎖。
boost::this_thread::sleep_for(boost::chrono::milliseconds(1)).

Boost 1.54.0

以下程式碼很可能重現死鎖:

#include "stdafx.h"

#include <iostream>
#include <boost/thread.hpp>

using namespace std;

void worker()
{
    for (int i = 0; i < 10000; i++)
        boost::this_thread::sleep_for(boost::chrono::milliseconds(1));
}

int _tmain(int argc, _TCHAR* argv[])
{
    boost::thread_group tg;
    for (int i = 0; i < 30; i++)
        tg.create_thread(worker);

    tg.join_all();
    cout << "All done!" << endl;
    return 0;
}

附加到死鎖程序除錯時,呼叫棧如下:
boost::this_thread::interruptible_wait() Line 547
boost::detail::basic_cv_list_entry::wait() Line 94
boost::detail::basic_condition_variable::do_wait<>() Line 228
boost::condition_variable::wait_until<>() Line 371
boost::this_thread::sleep_until<>() Line32
boost::this_thread::sleep_for<>() Line72
worker()
boost::detail::thread_data<>()

程式在停在thread.cpp:547:
    unsigned long const notified_index=detail::win32::WaitForMultipleObjects(handle_count, handles, false, using_timer?INFINITE:time_left.milliseconds);
其中    
handle_count = 2
using_timer = false
time_left.milliseconds = 0xfffffffe

發現 basic_cv_list_entry::wait(timeout abs_time) 當 abs_time.milliseconds = uint64(-1) 時就會死鎖.
所在可以在此斷點,將 abs_time.milliseconds 改為 uint64(-1),就可以重現死鎖了.

實際上該引數很可能輸入為-1:
        wait_until(...)
        {
          do_wait(lock, ceil<milliseconds>(t-Clock::now()).count());
          ...
        }


這幾天改伺服器程式碼,添加了許多 sleep_for(), 結果就經常發現伺服器停止響應了。
幸好該錯誤僅在Windows上存在。錯誤位於 boost/thread/win32/condition_variable.hpp.

正準備給Boost新增條Bug記錄,發現該錯誤已在3周前報告,10天前已修正,將在1.55.0版本中釋出。
見:
Condition variable will wait forever for some timepoint values (MSVC)    
https://svn.boost.org/trac/boost/ticket/9079

修正程式碼如下:

    chrono::time_point<Clock, Duration> now = Clock::now();
    if (t<=now) {
      return cv_status::timeout;
    }
    do_wait(lock, ceil<milliseconds>(t-now).count());