Следующая программа работает по-разному в зависимости от используемого механизма сна.
#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;
}
Разница в том, что с sleep_for
Исходя из того, что я вижу, задачи планируются таким образом, что 1 сон не мешает другим запускаться.
С Sleep
из того, что я вижу, они блокируют выполнение дальнейших задач.
Мои вопросы:
а) как пул потоков PPL может сделать хитрый трюк, который позволяет его обойти sleep_for
(Я думал, что это системный вызов, который сообщает ОС (поместите этот поток в список неактивных потоков на x секунд)
б) Поведение, которое я вижу здесь с sleep_for
гарантировано (иначе определено, что я не получу такое же поведение, как с Sleep
)
а) как пул потоков PPL может сделать хитрый трюк, который позволяет его обойти
sleep_for
(Я думал, что это системный вызов, который сообщает ОС (поместите этот поток в список неактивных потоков на x секунд)
В Microsoft VC ++ 2012 библиотека потоков C ++ Standard и PPL реализованы на основе Параллельное время выполнения (ConcRT) у которого есть планировщик кооперативов и краж.
Так что планировщик ConcRT может обрабатывать задачи, включая std::this_thread::sleep_for
умный путь.
б) Гарантируется ли поведение, которое я вижу здесь, с sleep_for (иначе определено, что я не получу такое же поведение, как с
Sleep
)
Возможно, нет. Sleep
это родной WinAPI, я думаю, планировщик ConRT не может относиться к этому совместно.
Примечание: Microsoft говорит ConcRT использует планирование в пользовательском режиме (UMS) в Windows 7 / Server 2008 R2 64-битный издание. Поведение может быть изменено на такой платформе …
Возможно, информативно взглянуть на соответствующий заголовок (т.е. #include <thread>
) и посмотрим, чем он отличается. Например, это sleep_for()
определение:
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);
}
Который использует перегрузку «абсолютного времени» 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);
}
Там тропа останавливается на вызове CRT _Thrd_sleep()
и не так просто понять, что делает условный код. Похоже, что он выполняет другие действия для планировщика не по умолчанию, но я не могу понять, почему в данный момент. В любом случае, возможно, это пролило немного свет?
Одной из причин может быть то, что точность двух методов сна различна. Чтобы выяснить это, вы должны проверить — на вашей платформе — на точность, измерив время между двумя соседними Sleep(0)
и два соседних Sleep(1)
и то же самое для sleep_for()
Имейте в виду, что между точностью значений таймера и их фактической точностью может быть существенная разница. Например. Функция clock () обеспечивает точность в миллисекундах, но — на моей машине — имеет точность 15 мс (в среднем).