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.
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.
You can use a ManualResetEventSlim:
var signal = new ManualResetEventSlim();
Maintenance.RecievedMessage += delegate { signal.Set(); };
Maintenance.checkLastXML = false;
Maintenance.NeedToUpdateFromCarrier(userId);
signal.Wait();
return true;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With