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:
- Thread.Yield(): If there is another thread ready to execute, then this gives away the current CPU core to that thread. Otherwise does nothing, i.e. returns immediately, which may cause high CPU (and high battery consumption) if the system has at least one idle CPU core. In most cases, resumes execution in the same CPU core, but this is not guaranteed for a number of reasons (the thread might be suspended by GC, etc). On server space, it's a great way to implement a simple busy wait, but it may be slower than the above explained
SpinWait
in some cases. On Windows systems, it is (as of this writing) implemented as call to Win32 API SwitchToThread.
- Thread.Sleep(0): Puts the current thread in the end of OS ready-to-execute queue for that priority, if the queue is not empty. Otherwise does nothing, i.e. returns immediately. The behavior is almost identical to
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.Sleep(1): If the remainder of the timeslice has at least 1ms, gives the CPU away for the remainder of the timeslice. Otherwise, gives the CPU away for the remainder of the timeslice AND the entire next timeslice. Because in many systems the timeslice is about 15ms, it tends to sleep for about 7ms or 8ms in average. Therefore, different than
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.
Threading.Timer
for this polling process? If you are using .NET 4.0 you can mix that in with the TPL to have task-based cooperative cancellation. – Lebanon