C ++ 11线程等待行为:std :: this_thread :: yield()vs. std :: this_thread :: sleep_for(std :: chrono :: milliseconds(1)) [英] C++11 Thread waiting behaviour: std::this_thread::yield() vs. std::this_thread::sleep_for( std::chrono::milliseconds(1) )

查看:3360
本文介绍了C ++ 11线程等待行为:std :: this_thread :: yield()vs. std :: this_thread :: sleep_for(std :: chrono :: milliseconds(1))的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我编写Microsoft特定的C ++代码时,我被告知编写 Sleep(1) 比自旋锁的优点比 Sleep(0)事实上, Sleep(0)将使用更多的CPU时间,此外,它只会产生是否有另一个等于优先级的线程等待运行。



然而,对于C ++ 11线程库,没有太多的文档(至少我已经能够找到) code> std :: this_thread :: sleep_for(std :: chrono :: milliseconds(1)) std :: this_thread :: yield() code>;第二个肯定是更冗长,但是它们对于自旋锁同样有效,或者受到潜在的影响 Sleep(0) Sleep(1)



std :: this_thread :: yield std :: this_thread :: sleep_for(std :: chrono :: milliseconds(1))可接受:

  void SpinLock(const bool& bSomeCondition)
{
//等待一些条件满足
while !bSomeCondition)
{
/ * std :: this_thread :: yield()或
std :: this_thread :: sleep_for(std :: chrono :: milliseconds(1))
在这里是可以接受的。* /
}

//做点什么!
}


解决方案

,因为一个具体的实现将在很大程度上受底层操作系统的调度能力的影响。



话虽如此,你可以在任何现代操作系统上安全地假设几件事情:




  • 将放弃当前时间片,并将线程重新插入调度队列。直到再次执行线程为止的时间量通常完全取决于调度程序。请注意,该标准将收益率视为重新安排的机会。所以一个实现是完全自由的,如果它想要立即返回。收益率永远不会将线程标记为非活动,因此线程在收益率上旋转将始终在一个核心上产生100%的负载。

  • sleep _ * 将阻塞线程至少所请求的时间量。实现可以将 sleep_for(0)变成 yield 。另一方面, sleep_for(1)会将您的线程暂停。而不是回到调度队列,线程首先进入睡眠线程的不同队列。只有在所请求的时间量已经过去之后,调度器才会考虑将线程重新插入调度队列中。小睡眠产生的负荷仍然很高。如果请求的休眠时间小于系统时间片,则可以预期该线程将仅跳过一个时间片(即,一个产量释放活动时间片,然后跳过一个时间片),这仍将导致cpu负载


  • 关于旋转锁定的几个词是更好的。自旋锁是一个选择的工具,当希望在锁几乎没有争用。如果在绝大多数情况下,你希望锁可用,自旋锁是一个便宜和有价值的解决方案。但是,一旦你有争用,自旋锁将会花费你。如果你担心产量或睡眠是否是更好的解决方案,自旋锁是工作的错误工具。您应该使用互斥体。



    对于自旋锁,实际上必须等待锁的情况应该被视为异常。因此,只是屈服在这里 - 它表达的意图清楚和浪费CPU时间应该永远不应该是一个问题的第一位是完全没有问题。


    I was told when writing Microsoft specific C++ code that writing Sleep(1) is much better than Sleep(0) for spinlocking, due to the fact that Sleep(0) will use more of the CPU time, moreover, it only yields if there is another equal-priority thread waiting to run.

    However, with the C++11 thread library, there isn't much documentation (at least that I've been able to find) about the effects of std::this_thread::yield() vs. std::this_thread::sleep_for( std::chrono::milliseconds(1) ); the second is certainly more verbose, but are they both equally efficient for a spinlock, or does it suffer from potentially the same gotchas that affected Sleep(0) vs. Sleep(1)?

    An example loop where either std::this_thread::yield() or std::this_thread::sleep_for( std::chrono::milliseconds(1) ) would be acceptable:

    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!
    }
    

    解决方案

    The Standard is somewhat fuzzy here, as a concrete implementation will largely be influenced by the scheduling capabilities of the underlying operating system.

    That being said, you can safely assume a few things on any modern OS:

    • yield will give up the current timeslice and re-insert the thread into the scheduling queue. The amount of time that expires until the thread is executed again is usually entirely dependent upon the scheduler. Note that the Standard speaks of yield as an opportunity for rescheduling. So an implementation is completely free to return from a yield immediately if it desires. A yield will never mark a thread as inactive, so a thread spinning on a yield will always produce a 100% load on one core. If no other threads are ready, you are likely to lose at most the remainder of the current timeslice before you get scheduled again.
    • sleep_* will block the thread for at least the requested amount of time. An implementation may turn a sleep_for(0) into a yield. The sleep_for(1) on the other hand will send your thread into suspension. Instead of going back to the scheduling queue, the thread goes to a different queue of sleeping threads first. Only after the requested amount of time has passed will the scheduler consider re-inserting the thread into the scheduling queue. The load produced by a small sleep will still be very high. If the requested sleep time is smaller than a system timeslice, you can expect that the thread will only skip one timeslice (that is, one yield to release the active timeslice and then skipping the one afterwards), which will still lead to a cpu load close or even equal to 100% on one core.

    A few words about which is better for spin-locking. Spin-locking is a tool of choice when expecting little to no contention on the lock. If in the vast majority of cases you expect the lock to be available, spin-locks are a cheap and valuable solution. However, as soon as you do have contention, spin-locks will cost you. If you are worrying about whether yield or sleep is the better solution here spin-locks are the wrong tool for the job. You should use a mutex instead.

    For a spin-lock, the case that you actually have to wait for the lock should be considered exceptional. Therefore it is perfectly fine to just yield here - it expresses the intent clearly and wasting CPU time should never be a concern in the first place.

    这篇关于C ++ 11线程等待行为:std :: this_thread :: yield()vs. std :: this_thread :: sleep_for(std :: chrono :: milliseconds(1))的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆