Alternative to SpinWait.SpinUntil

13,991

Solution 1

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. 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). It costs between 2000 and 10,000 cycles.

Those are cpu cycles that don't accomplish much. 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. 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. 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. 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. Because you increase the odds that the thread that sets the wait condition can get enough cpu cycles to finish its job.

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. And most convincingly, you are seeing lots of cpu being burned. 1% of load in TaskMgr.exe is about 20 million cpu cycles.

Use a waitable event instead, like AutoResetEvent. Note the structural change required, isCompleted can't be a bool anymore. You call Set() in the completion handler, Wait() to block on it.

Solution 2

You can use a ManualResetEventSlim:

var signal = new ManualResetEventSlim();

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

signal.Wait();
return true;
Share:
13,991
Fore
Author by

Fore

Updated on June 04, 2022

Comments

  • Fore
    Fore almost 2 years

    We are using Task in our application. 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); 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.

    Since SpinWait.SpinUntil is very heavy for the CPU, I'm now looking for an alternative solution to this problem.

  • Fore
    Fore over 12 years
    Looks nice, I will try it out. Do you know if it's resource intensive like spinwait?
  • Feidex
    Feidex over 12 years
    It's quite lightweight. The thread calling signal.Wait(); will just sleep until the signal is set.
  • Fore
    Fore over 12 years
    Thanks for this very detailed answere. This is my first go at using Tasks. Would you agree with dtb's solution? Or how would you solve it, please help me with a litle code snippet.
  • user1703401
    user1703401 over 12 years
    Yes, dtb offered the same solution. I just focused on why it is the proper solution. Doing the fish.
  • Error404
    Error404 over 3 years
    The Slim variant is optimized for short wait times and spin waits for a while before going to sleep.