为什么 sleep_for 调用 FreeLibrary?
Why sleep_for calls FreeLibrary?
我很震惊地追踪到这个简单的代码:
#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();
}
你猜怎么着? sleep_for 每次都调用 FreeLibrary !
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
为什么 sleep_for 必须调用 FreeLibrary?
此程序使用 boost 库需要 2 秒,使用 msvcrt(发布模式)需要 > 3 分钟(失去耐心)。我无法想象。
在 Visual C++ 2013 中,大多数 C++ 标准库并发功能位于 Concurrency Runtime (ConcRT) 之上。 ConcRT 是一种工作窃取运行时,可提供协作调度和阻塞。
这里,Concurrency::wait
使用线程池计时器来执行等待。它使用 LoadLibrary
/FreeLibrary
在计时器挂起期间递增托管 ConcRT 运行时的模块的引用计数。这样可以确保模块在等待期间不会被卸载。
我不是 ConcRT 专家(甚至不是专家),所以我不能 100% 确定可以在此处卸载 ConcRT 模块的确切场景是什么。我知道我们对 std::thread
和 _beginthreadex
进行了类似的更改,以获取对托管线程回调的模块的引用,以确保在线程执行时不会卸载该模块。
在 Visual C++ 2015 中,C++ 标准库并发功能已修改为直接位于 Windows 操作系统原语(例如 CreateThread
、Sleep
等)之上,而不是 ConcRT .这样做是为了提高性能,解决将 C++ 线程功能与操作系统功能混合使用时的正确性问题,并且作为对 ConcRT 的更普遍的弱化的一部分。
请注意,在 Windows 上,睡眠精度以毫秒为单位,零毫秒的睡眠通常意味着 "go do other useful work before coming back to me." 如果您使用 Visual C++ 2015 编译程序,则每次调用 wait_for
将依次调用 Sleep(0)
,其中 "causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run."
我很震惊地追踪到这个简单的代码:
#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();
}
你猜怎么着? sleep_for 每次都调用 FreeLibrary !
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
为什么 sleep_for 必须调用 FreeLibrary?
此程序使用 boost 库需要 2 秒,使用 msvcrt(发布模式)需要 > 3 分钟(失去耐心)。我无法想象。
在 Visual C++ 2013 中,大多数 C++ 标准库并发功能位于 Concurrency Runtime (ConcRT) 之上。 ConcRT 是一种工作窃取运行时,可提供协作调度和阻塞。
这里,Concurrency::wait
使用线程池计时器来执行等待。它使用 LoadLibrary
/FreeLibrary
在计时器挂起期间递增托管 ConcRT 运行时的模块的引用计数。这样可以确保模块在等待期间不会被卸载。
我不是 ConcRT 专家(甚至不是专家),所以我不能 100% 确定可以在此处卸载 ConcRT 模块的确切场景是什么。我知道我们对 std::thread
和 _beginthreadex
进行了类似的更改,以获取对托管线程回调的模块的引用,以确保在线程执行时不会卸载该模块。
在 Visual C++ 2015 中,C++ 标准库并发功能已修改为直接位于 Windows 操作系统原语(例如 CreateThread
、Sleep
等)之上,而不是 ConcRT .这样做是为了提高性能,解决将 C++ 线程功能与操作系统功能混合使用时的正确性问题,并且作为对 ConcRT 的更普遍的弱化的一部分。
请注意,在 Windows 上,睡眠精度以毫秒为单位,零毫秒的睡眠通常意味着 "go do other useful work before coming back to me." 如果您使用 Visual C++ 2015 编译程序,则每次调用 wait_for
将依次调用 Sleep(0)
,其中 "causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run."