3

Following program runs differently depending on the sleep mechanism used.

#include <ppltasks.h>
#include <chrono>
#include <thread>
#include <iostream>
#include <atomic>
#include <windows.h>
using namespace std;
#define MODERN_MAN
int main()
{
    atomic<int> cnt;
    concurrency::task_group tg;
    for (int i =0; i<1000; ++i )
    {
        tg.run([&cnt](){cout << "."; cnt++; 
        #ifdef MODERN_MAN
        this_thread::sleep_for(chrono::seconds(5));
        #else
        Sleep(5000);
        #endif
        });
    }
    tg.wait();
    cout << cnt;
    return 0;
}

Difference is that with sleep_for from what I see tasks get scheduled in a way that 1 sleeping doesnt prevent others from running.

With Sleep from what I see they block further tasks from running.

My questions are:
a) how can PPL thread pool do the clever trick that enables it to workaround sleep_for ( I thought it is a system call that tells OS (put this thread in the list of inactive threads for x seconds)

b) Is the behavior Im seeing here with sleep_for guaranteed(aka is it defined that I wont get same behavior as with Sleep)

ildjarn
  • 62,044
  • 9
  • 127
  • 211
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277

3 Answers3

4

a) how can PPL thread pool do the clever trick that enables it to workaround sleep_for ( I thought it is a system call that tells OS (put this thread in the list of inactive threads for x seconds)

On Microsoft VC++2012, C++ Standard threading library and PPL are implemented based on Concurrency Runtime(ConcRT) which has cooperative and work-stealing scheduler. So ConcRT scheduler can treat tasks includes std::this_thread::sleep_for clever way.

b) Is the behavior Im seeing here with sleep_for guaranteed(aka is it defined that I wont get same behavior as with Sleep)

Maybe NO. Sleep is native WinAPI, I guess ConRT scheduler can't treat it cooperatively.

Side note: Microsoft says ConcRT use User-mode scueduling(UMS) on Windows 7/Server 2008 R2 64-bit edition. The behavior may be changed on such platform...

yohjp
  • 2,985
  • 3
  • 30
  • 100
  • 1
    See also the "Coordinating Tasks with Cooperative Blocking" section of. http://msdn.microsoft.com/en-us/library/gg663529.aspx – Ade Miller Apr 02 '13 at 17:03
2

It's possibly informative to take a look at the relevant header (i.e. #include <thread>) and see how it differs. For example, this is the sleep_for() definition:

template<class _Rep, class _Period> inline
void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time)
{
    // sleep for duration
    stdext::threads::xtime _Tgt = _To_xtime(_Rel_time);
    sleep_until(&_Tgt);
}

Which uses the "absolute time" overload of sleep_until():

inline void sleep_until(const stdext::threads::xtime *_Abs_time)
{   
    // sleep until _Abs_time
    if (::Concurrency::details::_CurrentScheduler::_Id() != -1)
    {
        stdext::threads::xtime _Now;
        stdext::threads::xtime_get(&_Now, stdext::threads::TIME_UTC);
        ::Concurrency::wait(_Xtime_diff_to_millis2(_Abs_time, &_Now));
        return;
    }
    _Thrd_sleep(_Abs_time);
}

There the trail stops at the CRT call to _Thrd_sleep(), and it's not so easy to figure out what the conditional code is doing. It looks like it's taking different action for a non-default scheduler, but I can't see why at the moment. In any case, perhaps that has shed some light?

Roger Rowland
  • 25,885
  • 11
  • 72
  • 113
1

One reason can be that the accuracy of the two sleep methods are different. To find out you should test - on your platform - for the accurady by measuring the time between two adjacent Sleep(0) and two adjacent Sleep(1) and the same for sleep_for()

Please be aware, that there might be a significant difference between the precision of the timer values and their actual accuracy. E.g. clock() provides millisecond precision but - on my machine - has an accuracy of 15ms (on average).

ogni42
  • 1,232
  • 7
  • 11