简体   繁体   English

C#SpinWait用于长期等待

[英]C# SpinWait for long-term waiting

This code consumes near zero CPU (i5 family) 此代码消耗接近零CPU(i5系列)

    public void SpinWait() {

        for (int i = 0; i < 10000; i++)
        {
            Task.Factory.StartNew(() =>
            {
                var sw = new SpinWait();
                while (true)
                {
                    sw.SpinOnce();
                }
            });
        }
    }

In my code performance difference compared to SemaphoreSlim is 3x times or more for the case when spinning is really justified (5 Mops). 在我的代码中,与SemaphoreSlim相比,性能差异是旋转真正合理(5 Mops)的情况下的3倍或更多。 However, I am concerned about using it for long-term wait. 但是,我担心使用它进行长期等待。 The standard advice is to implement two-phase wait operation. 标准建议是实现两阶段等待操作。 I could check NextSpinWillYield property and introduce a counter+reset to increase the default spinning iterations without yielding, and than back off to a semaphore. 我可以检查NextSpinWillYield属性并引入计数器+重置以增加默认的旋转迭代而不会屈服,而不是退回到信号量。

But what are downsides of using just SpinWait.SpinOnce for long-term waiting? 但是使用SpinWait.SpinOnce进行长期等待的缺点是什么? I have looked through its implementation and it properly yields when needed. 我已经查看了它的实现,并在需要时正确地生成了它。 It uses Thread.SpinWait which on modern CPUs uses PAUSE instruction and is quite efficient according to Intel . 它使用Thread.SpinWait ,它在现代CPU上使用PAUSE指令,并且根据英特尔的说法非常有效。

One issue that I have found while monitoring Task Manager is that number of threads if gradually increasing due to the default ThreadPool algorithm (it adds a thread every second when all tasks are busy). 我在监视任务管理器时发现的一个问题是,由于默认的ThreadPool算法逐渐增加线程数(它在所有任务繁忙时每秒添加一个线程)。 This could be solved by using ThreadPool.SetMaxThreads , and then the number of threads is fixed and CPU usage is still near zero. 这可以通过使用ThreadPool.SetMaxThreads来解决,然后修复线程数并且CPU使用率仍然接近于零。

If the number of long-waiting tasks is bounded, what are other pitfalls of using SpinWait.SpinOnce for long-term waiting. 如果长时间等待任务的数量有限,那么使用SpinWait.SpinOnce进行长期等待会有什么其他陷阱。 Does it depend on CPU family, OS, .NET version? 它是否依赖于CPU系列,OS,.NET版本?

(Just to clarify: I will still implement two-phase waiting, I am just curios why not using SpinOnce all the time?) (只是为了澄清:我仍然会实现两阶段等待,我只是好奇为什么不一直使用SpinOnce?)

Well, the down-side is exactly the one you see, your code is occupying a thread without accomplishing anything. 好吧,正面就是你看到的那个,你的代码占用了一个线程而没有完成任何事情。 Preventing other code from running and forcing the threadpool manager to do something about it. 防止其他代码运行并强制线程池管理器对其执行某些操作。 Tinkering with ThreadPool.SetMaxThreads() is just a band-aid on what is likely to be a profusely bleeding wound, only ever use it when you need to catch the plane home. 修补ThreadPool.SetMaxThreads()只是一个可能是一个大量出血伤口的创可贴,只有在你需要赶飞机回家时才使用它。

Spinning should only ever be attempted when you have a very good guarantee that doing so is more efficient than a thread context switch. 只有在非常好地保证这样做比线程上下文切换有效时,才应该尝试旋转。 Which means that you have to be sure that the thread can continue within ~10,000 cpu cycles or less. 这意味着您必须确保线程可以在大约10,000 cpu周期内继续运行。 That is only 5 microseconds , give or take, a wholeheckofalot less than what most programmers consider "long-term". 这只是5 微秒 ,给予或接受,比大多数程序员认为的“长期”更少。

Use a sync object that will trigger a thread context switch instead. 使用将触发线程上下文切换的同步对象。 Or the lock keyword. 或者是lock关键字。

Not only will that yield the processor so other waiting threads can get their job done, thus accomplishing a lot more work, it also provides an excellent cue to the OS thread scheduler. 这不仅会产生处理器,因此其他等待线程可以完成工作,从而完成更多工作,它还为OS线程调度程序提供了极好的提示。 A sync object that is signaled will bump the priority of the thread so it is very likely to get the processor next. 发出信号的同步对象将突破线程的优先级,因此很可能会使处理器接下来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM