Reputation: 3005
I have a method that uses a background worker to poll a DLL for a status looking something like this:
var timeout = DateTime.Now.AddSeconds(3);
while (System.Status != Status.Complete // our status is not complete
&& DateTime.Now < timeout // have not timed out
&& !_Worker.CancellationPending) // backgroundworker has not been canceled
{
//Thread.Yield();
//Thread.SpinWait(1);
//Thread.Sleep(1);
}
When looking at my CPU %, yield()
and spinwait()
cause my app to shoot up to 50% on my PC. With Sleep(1)
my CPU % stays down at 6%. I have been told that that I should choose Thread.Yield()
, however the spikes in CPU % bother me. What is best practice for something like this?
Upvotes: 37
Views: 33408
Reputation: 7439
Answering this question from the post message:
What is best practice for something like this?
You seem to have a case for the SpinWait struct, which exists since .NET 4.0 and is different than the much older SpinWait method.
Your code can be upgraded to something like this:
var timeout = DateTime.Now.AddSeconds(3);
var spinWait = new SpinWait();
while (System.Status != Status.Complete
&& DateTime.Now < timeout
&& !_Worker.CancellationPending)
{
spinWait.SpinOnce();
}
The SpinOnce
method will decide whether to call Thread.Sleep
, Thread.Yield
or do a "busy wait" where there is no yield.
Specifically about what the post title (i.e. Thread.Sleep vs Thread.Yield), here are some up-to-date answers:
SpinWait
in some cases. On Windows systems, it is (as of this writing) implemented as call to Win32 API SwitchToThread.Thread.Yield()
, except that it allows switching the CPU core, which tends to give more time to threads with same or lower priority before resuming. In addition to this additional time, the odds of resuming on a different CPU core are much greater, causing things like L1 or L2 cache to be lost, and further decrease performance. It should be used only when odds of starvation are higher than using Thread.Yield()
. On Windows systems, it is (as of this writing) implemented as call to Win32 API SleepEx.Thread.Yield()
and Thread.Sleep(0)
, the Thread.Sleep(1)
is guaranteed to give the CPU away for some time, which may cool things down. However, the performance cost is huge, and for that reason it should only be used in conjunction with other "spinning" or "yielding" solutions.Additional comment about this:
however the spikes in CPU % bother me
From your post, that concern might be because the code has a 3-second timeout. The SpinWait
should take care of that, but you can't use the SpinWait
struct for some reason, consider using a smaller timeout, in the order of milliseconds.
Upvotes: 5
Reputation: 150148
Thread.Yield will interrupt the current thread to allow other threads to do work. However, if they do not have any work to do, your thread will soon be rescheduled and will continue to poll, thus 100% utilization of 1 core.
Causes the calling thread to yield execution to another thread that is ready to run on the current processor. The operating system selects the thread to yield to.
Thread.Sleep will schedule your thread to run again after the sleep time expires, thus much lower CPU utilization.
Blocks the current thread for the specified number of milliseconds.
Given the choice between the two, Thread.Sleep
is better suited for your task. However, I agree with the comment from @Bryan that a Threading.Timer
makes for a more elegant solution.
Upvotes: 40