Tho*_*ell 34 c++ multithreading standard-library c++11
有人告诉我,当编写Microsoft特定的C++代码时,写入Sleep(1)
比自动Sleep(0)
锁定要好得多,因为它Sleep(0)
会占用更多的CPU时间,而且只有在等待运行的另一个等优先级线程时才会产生.
但是,对于C++ 11线程库,没有太多文档(至少我已经能够找到)关于std::this_thread::yield()
vs 的效果std::this_thread::sleep_for( std::chrono::milliseconds(1) )
; 第二当然是更冗长,但他们都同样有效的自旋锁呢,还是从潜在受影响相同遭遇的陷阱Sleep(0)
主场迎战Sleep(1)
?
一个示例循环,其中任何一个std::this_thread::yield()
或std::this_thread::sleep_for( std::chrono::milliseconds(1) )
可以接受:
void SpinLock( const bool& bSomeCondition )
{
// Wait for some condition to be satisfied
while( !bSomeCondition )
{
/*Either std::this_thread::yield() or
std::this_thread::sleep_for( std::chrono::milliseconds(1) )
is acceptable here.*/
}
// Do something!
}
Run Code Online (Sandbox Code Playgroud)
Com*_*sMS 31
标准在这里有点模糊,因为具体实现将在很大程度上受底层操作系统的调度功能的影响.
话虽这么说,你可以安全地假设任何现代操作系统上的一些东西:
yield
将放弃当前时间片并将线程重新插入调度队列.在线程再次执行之前到期的时间量通常完全取决于调度程序.请注意,标准将收益率视为重新安排的机会.因此,如果需要,实现可以完全自由地从收益中立即返回.yield永远不会将线程标记为非活动状态,因此在yield上旋转的线程将始终在一个核心上产生100%的负载.如果没有准备好其他线程,则在再次安排之前,您可能最多会丢失当前时间片的剩余部分.sleep_*
将阻止线程至少所需的时间.实现可能会sleep_for(0)
变成a yield
.在sleep_for(1)
另一方面,将发送您的线程进入暂停.线程首先进入不同的休眠线程队列,而不是回到调度队列.只有在请求的时间量过去之后,调度程序才会考虑将线程重新插入调度队列.小睡眠产生的负荷仍然很高.如果请求的休眠时间小于系统时间片,则可以预期线程将只跳过一个时间片(即,释放活动时间片然后跳过一个产生一个产量),这仍然会导致cpu负载在一个核心上接近甚至等于100%.关于哪一个更适合自旋锁定的几句话.当锁定期望几乎没有争用时,自旋锁定是一种可供选择的工具.如果在绝大多数情况下您希望锁定可用,则旋转锁是一种廉价且有价值的解决方案.但是,只要你有争用,旋转锁将花费你.如果你担心产量或睡眠是否是更好的解决方案,旋转锁是这项工作的错误工具.您应该使用互斥锁.
对于自旋锁,你实际上必须等待锁的情况应该被视为例外.因此,在这里屈服是完全可以的 - 它清楚地表达了意图,浪费CPU时间首先应该不是一个问题.
Rob*_*b L 14
我刚刚在Windows 7,2.8GHz Intel i7上使用Visual Studio 2013进行了测试,默认发布模式优化.
sleep_for(非零)出现睡眠,最小值约为1毫秒,并且在循环中不占用CPU资源,如:
for (int k = 0; k < 1000; ++k)
std::this_thread::sleep_for(std::chrono::nanoseconds(1));
Run Code Online (Sandbox Code Playgroud)
如果您使用1纳秒,1微秒或1毫秒,则此1,000次睡眠循环大约需要1秒.另一方面,yield()每个大约需要0.25微秒,但会将CPU旋转到100%的线程:
for (int k = 0; k < 4,000,000; ++k) (commas added for clarity)
std::this_thread::yield();
Run Code Online (Sandbox Code Playgroud)
std :: this_thread :: sleep_for((std :: chrono :: nanoseconds(0))似乎与yield()大致相同(此处未显示测试).
相比之下,锁定自旋锁的atomic_flag大约需要5纳秒.这个循环是1秒:
std::atomic_flag f = ATOMIC_FLAG_INIT;
for (int k = 0; k < 200,000,000; ++k)
f.test_and_set();
Run Code Online (Sandbox Code Playgroud)
此外,互斥量需要大约50纳秒,此循环为1秒:
for (int k = 0; k < 20,000,000; ++k)
std::lock_guard<std::mutex> lock(g_mutex);
Run Code Online (Sandbox Code Playgroud)
基于此,我可能会毫不犹豫地在自旋锁中加权,但我几乎肯定不会使用sleep_for.如果你认为你的锁会旋转很多而且担心cpu消耗,我会切换到std :: mutex,如果这在你的应用程序中是实用的.希望Windows中std :: mutex表现糟糕的日子已经过去了.
如果您在使用 yield 时对 CPU 负载感兴趣 - 这非常糟糕,除了一种情况 - (只有您的应用程序正在运行,并且您知道它基本上会吃掉您的所有资源)
这里有更多解释:
sleep()
或sleep_for()
也是一个错误,这将阻止线程执行,但您将在 CPU 上等待时间。不要误会,这是工作 CPU,但优先级最低。虽然以某种方式适用于简单的使用示例(sleep() 上的满载 cpu 是满载工作处理器的一半),但如果您想确保应用程序的责任,您会喜欢第三个示例:结合!:
std::chrono::milliseconds duration(1);
while (true)
{
if(!mutex.try_lock())
{
std::this_thread::yield();
std::this_thread::sleep_for(duration);
continue;
}
return;
}
Run Code Online (Sandbox Code Playgroud)这样的事情将确保,cpu 将在执行此操作时尽可能快地产生,而且 sleep_for() 将确保 cpu 在尝试执行下一次迭代之前将等待一段时间。这个时间当然可以动态(或静态)调整以满足您的需要
欢呼:)
归档时间: |
|
查看次数: |
20239 次 |
最近记录: |