SpinWait in lockless update
Asked Answered
B

1

4

While reading Albahari's Threading in C#, I've noticed that the "lock free update" pattern uses a SpinWait at the end of the cycle:

static void LockFreeUpdate<T> (ref T field, Func <T, T> updateFunction)
  where T : class
{
    var spinWait = new SpinWait();
    while (true)
    {
        // read
        T snapshot1 = field;

        // apply transformation
        T calc = updateFunction (snapshot1);

        // compare if not preempted
        T snapshot2 = Interlocked.CompareExchange (ref field, calc, snapshot1);

        // if succeeded, we're done
        if (snapshot1 == snapshot2) return;

        // otherwise spin
        spinWait.SpinOnce();
    }
}

Note the spinWait.SpinOnce() call at the end. Is this call needed only to yield the thread in a single-threaded environment, or does it have an additional purpose?

Bulletproof answered 13/6, 2016 at 21:24 Comment(0)
E
2

Short answer... Yes. The intent of SpinWait is to allow yielding the thread to the System so as not to starve processing due to SpinLock.

From MSDN

https://msdn.microsoft.com/en-us/library/system.threading.spinwait(v=vs.110).aspx

SpinWait encapsulates common spinning logic. On single-processor machines, yields are always used instead of busy waits, and on computers with Intel processors employing Hyper-Threading technology, it helps to prevent hardware thread starvation. SpinWait encapsulates a good mixture of spinning and true yielding.

SpinWait is a value type, which means that low-level code can utilize SpinWait without fear of unnecessary allocation overheads. SpinWait is not generally useful for ordinary applications. In most cases, you should use the synchronization classes provided by the .NET Framework, such as Monitor. For most purposes where spin waiting is required, however, the SpinWait type should be preferred over the SpinWait method.

Eleusis answered 13/6, 2016 at 21:50 Comment(8)
Hi Matthew, could you clarify this more? The CompareExchange is supposed to be a single CPU instruction. So in a multi-threaded environment, I would presume that at least one thread would succeed at performing the instruction every [very few cycles]. It follows that for the other threads there is little harm in looping, since they can never need to wait more than a few cycles. Do I understand this correctly? And if so, do you agree that the SpinWait only really helps a single-threaded environment?Vina
A spin lock will block the thread on a cpu. Even if it’s a multi-core/multi-thread cpu blocking a hardware thread can slow down processing (assuming locked for IO.) Spinlock would be fine if you blocking for something fast like CPU bound processing as the context switching would take more time than the block.Eleusis
As a note, if you are doing C# use async/await and don’t worry about any of this until you find you code doesn’t function fast enough.Eleusis
Also the CompareExhange isn’t where the spinlock would occur. It would be in the tight loop around this code.Eleusis
Right, I meant in the tight loop around the code. I understand that the Spin Lock will block. But so does simply looping without it. So the question remains, does the Spin Lock add anything (compared to looping without it) in a multi-threaded environment, for this particular scenario [of an operation that completes in very few cycles]?Vina
updateFunction is a delegate that could be doing next to anything. That could be IO of CPU bound.Eleusis
Fair point. For the sake of argument, suppose this was not a delegate-based method, but simply an ExchangeIfGreaterThan method. Do you know the answer to my question? Does the SpinLock add any value in a multi-threaded environment for that scenario?Vina
The spinlock would be better as you would be cpu bound and not IO. Doing the async/thread yield would add a ton of context switching overhead. But if that’s all you are doing I’d also suggest you don’t do any of this. A simple tight loop or a linq query would be better.Eleusis

© 2022 - 2024 — McMap. All rights reserved.