1

I'm shocked to trace this simple code:

#include <thread>

void foo()
{
    for (int i = 0; i < 1000000; ++i) {
        std::this_thread::sleep_for(std::chrono::nanoseconds(1));
    }
}

int main()
{
    std::thread t(foo);
    t.join();
}

Guess what ? sleep_for calls FreeLibrary everytime !

kernel32.dll!_FreeLibraryStub@4()
msvcr120d.dll!Concurrency::details::DeleteAsyncTimerAndUnloadLibrary(_TP_TIMER * timer) Line 707
msvcr120d.dll!Concurrency::details::_Timer::_Stop() Line 111
msvcr120d.dll!Concurrency::details::_Timer::~_Timer() Line 100
msvcr120d.dll!`Concurrency::wait'::`7'::TimerObj::~TimerObj()
msvcr120d.dll!Concurrency::wait(unsigned int milliseconds) Line 155
test826.exe!std::this_thread::sleep_until(const xtime * _Abs_time) Line 137
test826.exe!std::this_thread::sleep_for<__int64,std::ratio<1,1000000000> >(const std::chrono::duration<__int64,std::ratio<1,1000000000> > & _Rel_time) Line 162
test826.exe!foo() Line 6

Why sleep_for had to call FreeLibrary ?

This program will take 2 seconds with boost library, and will take > 3 minutes (lose my patience) with msvcrt (Release mode). I can't imagine.

4

1 回答 1

5

在 Visual C++ 2013 中,大多数 C++ 标准库并发功能位于并发运行时 (ConcRT)之上。ConcRT 是一个工作窃取运行时,提供协作调度和阻塞。

在这里,Concurrency::wait使用线程池计时器来执行等待。它使用LoadLibrary/FreeLibrary在计时器挂起期间增加托管 ConcRT 运行时的模块的引用计数。这确保了模块在等待期间不会被卸载。

我不是 ConcRT 专家(甚至不是很接近),所以我不能 100% 确定可以在这里卸载 ConcRT 模块的确切情况。我知道我们对 and 进行了类似的更改std::thread_beginthreadex以获取对托管线程回调的模块的引用,以确保在线程执行时不会卸载模块。

在 Visual C++ 2015 中,C++ 标准库并发功能被修改为直接位于 Windows 操作系统原语(例如,等)之上CreateThreadSleep而不是 ConcRT。这样做是为了提高性能,解决在混合使用 C++ 线程功能与使用操作系统功能时的正确性问题,以及作为更一般地去强调 ConcRT 的一部分。

请注意,在 Windows 上,睡眠精度以毫秒为单位,零毫秒的睡眠通常意味着“在回到我身边之前先去做其他有用的工作”。如果您使用 Visual C++ 2015 编译程序,则每次调用wait_for都会依次调用Sleep(0),这“导致线程将其时间片的剩余部分交给任何其他准备好运行的线程”。

于 2015-08-26T18:17:24.573 回答