Spinwait VS Sleep Waiting. Which One to Use

SpinWait vs Sleep waiting. Which one to use?

The best approach is to have some mechanism to actively detect the thing becoming true (rather than passively polling for it having become true); this could be any kind of wait-handle, or maybe a Task with Wait, or maybe an event that you can subscribe to to unstick yourself. Of course, if you do that kind of "wait until something happens", that is still not as efficient as simply having the next bit of work done as a callback, meaning: you don't need to use a thread to wait. Task has ContinueWith for this, or you can just do the work in an event when it gets fired. The event is probably the simplest approach, depending on the context. Task, however, already provides most-everything you are talking about here, including both "wait with timeout" and "callback" mechanisms.

And yes, spinning for 10 seconds is not great. If you want to use something like your current code, and if you have reason to expect a short delay, but need to allow for a longer one - maybe SpinWait for (say) 20ms, and use Sleep for the rest?


Re the comment; here's how I'd hook an "is it full" mechanism:

private readonly object syncLock = new object();
public bool WaitUntilFull(int timeout) {
if(CollectionIsFull) return true; // I'm assuming we can call this safely
lock(syncLock) {
if(CollectionIsFull) return true;
return Monitor.Wait(syncLock, timeout);
}
}

with, in the "put back into the collection" code:

if(CollectionIsFull) {
lock(syncLock) {
if(CollectionIsFull) { // double-check with the lock
Monitor.PulseAll(syncLock);
}
}
}

What's the purpose of Thread.SpinWait method?

It's used as a replacement for very short-term sleep calls.

When you do multi-threaded locking, if the resource you're attempting to acquire is already locked, you typically go to sleep and wait for it to become free. When you do this, you give up the rest of the time that you were allocated by the scheduler to use the processor so someone else can have a go.
Normally this is fine, especially for long waits, like waiting for IO, loads of other processes can run on the CPU while you're waiting for the disk spindle to rotate.

However, sometimes, you're waiting for a tiny amount of time. In these cases, you would normally give up your remaining time anyway and wait for all the other threads to do their thing before getting another go.. so you can cheat, instead of waiting, you sit there continually polling in a 'are we nearly there yet?' way. If the lock is only held for a fraction of your remaining time, this becomes a very effective means of waiting, its also very efficient as the scheduler doesn't have to get involved in rearranging all the other threads to use the time you give up if you waited normally.

Obviously, if you spin every time you want a lock, you're not going to be very popular, your app will become sluggish and use 100% CPU, but in very small doses, at the right time, it makes the app more responsive.

If you're now thinking 'when should I use it?', that's a tricky call to make - if you have a resource that is very often locked and unlocked very quickly, then a spinlock around that instead of a wait is a good idea (and then test your app for performance), if you try spinning for a short time, and then fall back to a normal wait, that's a reasonable way too. But generally, you will never need to use it.

For a windows service, which is better, a wait-spin or a timer?

I don't see you getting any benefit out of a timer. You are essentially behaving as a timer anyway with your sleep call, and you are not being a bandwidth hog since sleep yields up the time slice. Having an explicit timer wake up and call you is just going to complicate the code.

I wouldn't really say that you are doing a spin-wait, since I generally think of a spin-wait as something that doesn't sleep. It just burns all it's processor time waiting for the go signal.

Thread.sleep() vs Thread.onSpinWait

So you wanted to see a short example about Object and its long-available wait() and notify/All() methods? (They are already there in JLS 1.0, from 20+ years ago)

Say no more:

public class NotifyTest {
private boolean flag = false;
public synchronized boolean getFlag() {
return flag;
}
public synchronized void setFlag(boolean newFlag) {
flag = newFlag;
notifyAll();
}

public static void main(String[] args) throws Exception {
final NotifyTest test = new NotifyTest();

new Thread(new Runnable() {
@Override
public void run() {
System.out.printf("I am thread at %,d, flag is %b\n",
System.currentTimeMillis(), test.getFlag());
synchronized (test) {
try {
test.wait();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
System.out.printf("I am thread at %,d, flag is %b\n",
System.currentTimeMillis(), test.getFlag());
}
}).start();

System.out.printf("I am main at %,d, flag is %b\n",
System.currentTimeMillis(), test.getFlag());
Thread.sleep(2000);
test.setFlag(true);
System.out.printf("I am main at %,d, flag is %b\n",
System.currentTimeMillis(), test.getFlag());
}
}

If your wait loop has anything else to do, Object.wait() has variants with timeout too.

So objects can be wait()-ed on and then waiting threads can be notified (one of the waiters via notify() or all of them via notifyAll()), and they do not even have to know about each other.

As both waiting and notifying has to happen inside a synchronized block, it is safe and possible to start the block, check the variable/flag/anything, and issue the wait conditionally (just these constructs are not shown here).

C# SpinWait for long-term waiting

Well, the down-side is exactly the one you see, your code is occupying a thread without accomplishing anything. Preventing other code from running and forcing the threadpool manager to do something about it. Tinkering with ThreadPool.SetMaxThreads() is just a band-aid on what is likely to be a profusely bleeding wound, only ever use it when you need to catch the plane home.

Spinning should only ever be attempted when you have a very good guarantee that doing so is more efficient than a thread context switch. Which means that you have to be sure that the thread can continue within ~10,000 cpu cycles or less. That is only 5 microseconds, give or take, a wholeheckofalot less than what most programmers consider "long-term".

Use a sync object that will trigger a thread context switch instead. Or the lock keyword.

Not only will that yield the processor so other waiting threads can get their job done, thus accomplishing a lot more work, it also provides an excellent cue to the OS thread scheduler. A sync object that is signaled will bump the priority of the thread so it is very likely to get the processor next.

When to use Task.Delay, when to use Thread.Sleep?

Use Thread.Sleep when you want to block the current thread.

Use await Task.Delay when you want a logical delay without blocking the current thread.

Efficiency should not be a paramount concern with these methods. Their primary real-world use is as retry timers for I/O operations, which are on the order of seconds rather than milliseconds.

Thread.sleep() vs Thread.onSpinWait

So you wanted to see a short example about Object and its long-available wait() and notify/All() methods? (They are already there in JLS 1.0, from 20+ years ago)

Say no more:

public class NotifyTest {
private boolean flag = false;
public synchronized boolean getFlag() {
return flag;
}
public synchronized void setFlag(boolean newFlag) {
flag = newFlag;
notifyAll();
}

public static void main(String[] args) throws Exception {
final NotifyTest test = new NotifyTest();

new Thread(new Runnable() {
@Override
public void run() {
System.out.printf("I am thread at %,d, flag is %b\n",
System.currentTimeMillis(), test.getFlag());
synchronized (test) {
try {
test.wait();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
System.out.printf("I am thread at %,d, flag is %b\n",
System.currentTimeMillis(), test.getFlag());
}
}).start();

System.out.printf("I am main at %,d, flag is %b\n",
System.currentTimeMillis(), test.getFlag());
Thread.sleep(2000);
test.setFlag(true);
System.out.printf("I am main at %,d, flag is %b\n",
System.currentTimeMillis(), test.getFlag());
}
}

If your wait loop has anything else to do, Object.wait() has variants with timeout too.

So objects can be wait()-ed on and then waiting threads can be notified (one of the waiters via notify() or all of them via notifyAll()), and they do not even have to know about each other.

As both waiting and notifying has to happen inside a synchronized block, it is safe and possible to start the block, check the variable/flag/anything, and issue the wait conditionally (just these constructs are not shown here).

SpinLock doesn't really do busy-loop waiting?

Regarding your edited question, yes busy spinning will give you the lowest latency of communication between the threads but you will pay for it by burning CPU time.

On the other hand you can use SpinWait which is less aggressive or you can code something in between such as this.

It comes down to a trade-off on how much you value latency and how many cores you have to spare.



Related Topics



Leave a reply



Submit