简体   繁体   English

SpinWait.SpinUntil的替代品

[英]Alternative to SpinWait.SpinUntil

We are using Task in our application. 我们在我们的应用程序中使用Task。 In one class we want to trigger an update that is running on a parallel task. 在一个类中,我们希望触发在并行任务上运行的更新。 The call looks like: 电话看起来像:

            Maintenance.RecievedMessage += new NotificationHandler(Maintenance_RecievedMessage);
            Maintenance.checkLastXML = false;
            Maintenance.NeedToUpdateFromCarrier(userId);

        SpinWait.SpinUntil(() => isCompleted == true);
        return true;

So we hook up an event that is triggered when the Maintenance.NeedToUpdateFromCarrier(userId); 所以我们挂钩了一个在Maintenance.NeedToUpdateFromCarrier(userId); method is done running. 方法运行完毕。 The complete method looks like: 完整的方法如下:

 private void Maintenance_RecievedMessage(IsCompleted changeargs)
    {
        isCompleted = true;
    }

So we are waiting for the NeedToUpdateFromCarrier method, as soon as it's done it triggers the event that its done, and we catch the event and set the property isComplete to true, and thats when the SpinWait.SpinUntil finnaly is done, and we continue. 所以我们正在等待NeedToUpdateFromCarrier方法,一旦它完成就触发它完成的事件,我们捕获事件并将属性isComplete设置为true,这就是当SpinWait.SpinUntil完成时,我们继续。

Since SpinWait.SpinUntil is very heavy for the CPU, I'm now looking for an alternative solution to this problem. 由于SpinWait.SpinUntil对CPU非常重,我现在正在寻找这个问题的替代解决方案。

It is important to understand when spin-waiting is appropriate. 重要的是要了解何时旋转等待是合适的。 There are very few cases where it is. 它的情况非常少。 Spin-waiting optimizes thread context switching. 旋转等待优化线程上下文切换。 Whenever you wait for something, a call like WaitHandle.Wait() will block the thread and yield the processor. 无论何时等待某事,WaitHandle.Wait()之类的调用都会阻塞线程并产生处理器。 The operating system performs a thread context switch when it finds some other thread to perform useful work. 当操作系统找到一些其他线程来执行有用的工作时,它会执行线程上下文切换。

Thread context switches are pretty expensive. 线程上下文切换非常昂贵。 There's no exact number because it depends on where the yield-to thread runs, there's extra overhead when that thread runs in another process or protection ring (drivers). 没有确切的数字,因为它取决于yield-to thread运行的位置,当该线程在另一个进程或保护环(驱动程序)中运行时会产生额外的开销。 It costs between 2000 and 10,000 cycles. 它的成本在2000到10,000个周期之间。

Those are cpu cycles that don't accomplish much. 那些是cpu周期,并没有取得多大成就。 Just overhead that doesn't get real work done. 只是开销没有完成真正的工作。 You can optimize your threading code if you know that it always takes less than 20,000 cycles for the wait condition to be met. 如果您知道等待条件总是需要少于20,000个周期,则可以优化线程代码。 Just delaying your thread (spinning) will then ensure that the expensive context switching isn't needed. 只是延迟你的线程(旋转)将确保不需要昂贵的上下文切换。 This is not the normal kind of delay like Thread.Sleep(), that yields, it is small loop that burns 100% core. 这不是像Thread.Sleep()这样的正常延迟,它会产生,它是一个小循环,可以燃烧100%核心。 With a few smarts thrown in, like spinning on a machine with only one core will never work well so it yields anyway. 抛出一些聪明的东西,就像在只有一个核心的机器上旋转一样,永远不会有效,所以它无论如何都会产生效果。

Clearly this will not work well if the wait condition consistently takes more than 20,000 cycles. 显然,如果等待条件持续超过20,000个周期,这将无法正常工作。 Now you're on the other end of the wise choice, you do want to yield in those cases. 现在你处于明智选择的另一端,你确实希望在那些情况下屈服。 Not just to avoid burning cpu when it doesn't accomplish anything but especially so because yielding makes it now more likely that the wait condition will be met sooner. 不仅仅是为了避免在没有完成任何事情时烧掉cpu,尤其是因为屈服使得现在更有可能更快地满足等待条件。 Because you increase the odds that the thread that sets the wait condition can get enough cpu cycles to finish its job. 因为增加设置等待条件的线程可以获得足够的cpu周期来完成其工作的几率。

There's plenty of evidence that's the case in your code. 有很多证据表明你的代码就是这种情况。 You explicitly ask code to do something before spinning. 您明确要求代码在旋转之前执行某些操作。 And it requires an event handler to signal the completion. 它需要一个事件处理程序来指示完成。 Mucho code needs to run. Mucho代码需要运行。 And most convincingly, you are seeing lots of cpu being burned. 最令人信服的是,你看到很多cpu被烧毁了。 1% of load in TaskMgr.exe is about 20 million cpu cycles. TaskMgr.exe中1%的负载大约是2000万个cpu周期。

Use a waitable event instead, like AutoResetEvent. 请使用等待事件,例如AutoResetEvent。 Note the structural change required, isCompleted can't be a bool anymore. 注意所需的结构变化,isCompleted不再是bool。 You call Set() in the completion handler, Wait() to block on it. 你在完成处理程序中调用Set(),Wait()来阻止它。

You can use a ManualResetEventSlim : 您可以使用ManualResetEventSlim

var signal = new ManualResetEventSlim();

Maintenance.RecievedMessage += delegate { signal.Set(); };
Maintenance.checkLastXML = false;
Maintenance.NeedToUpdateFromCarrier(userId);

signal.Wait();
return true;

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

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