2

I have the following code

class Program
    {
        static SpinLock sl = new SpinLock();

        public static void Foo(object arg)
        {
            bool r = false;
            sl.Enter(ref r);
        }
        public static void Main(string[] args)
        {
            bool r = false;
            sl.Enter(ref r);
            ThreadPool.QueueUserWorkItem(Foo);
            Thread.Sleep(5000);
            sl.Exit();
            Thread.Sleep(5000);
        }
    }

When I execute it, I see ~0% of the CPU load throughout the whole program run. I was expecting to see some CPU burn due to spinning inside the sl.Enter() call, but this is not the case.

According to SpinLock source, it uses SpinWait under the hood, which in turn actually calls Thread.Sleep(1) at some point.

Does that mean that both SpinLock and SpinWait aren't really "spinners", but instead still resort to the OS scheduler to yield some CPU shares?

EDIT

The question can be reformulated in the following way:

Using busy-looping ensures that the time between one thread releasing a lock and another thread acquiring it is minimal. Can I rely on SpinLock/SpinWait in this regard? From what I can tell, the answer is no, because it does Sleep for at least 1 microsecond, and the lock might be freed somewhere during this time.

arrowd
  • 33,231
  • 8
  • 79
  • 110
  • Related: [Is it really busy waiting (spinning) if I use `Thread.Sleep()`?](https://stackoverflow.com/questions/24630624/is-it-really-busy-waiting-if-i-thread-sleep) – John Wu Dec 09 '21 at 18:36
  • From the [docs](https://learn.microsoft.com/en-us/dotnet/api/system.threading.spinwait?view=net-6.0#remarks): _"`SpinWait` encapsulates a good mixture of spinning and true yielding."_ – Guru Stron Dec 09 '21 at 18:41
  • @GuruStron I thought that sentence is related to the previous one, "On single-processor machines, yields are always used instead of busy waits", however I run my code on a SMP machine. – arrowd Dec 09 '21 at 19:23
  • I have reformulated the question so that it doesn't involve opinion-based answers. – arrowd Dec 09 '21 at 19:28
  • 2
    The basic approach in SpinLock is to optimize context switching, spinning for a while before giving up and letting the OS block the thread to allow another one to run. Where "for a while" needs to be less than twice the cost of a context switch to actually optimize anything. A context switch takes between 3,000 and 15,000 processor cycles, give or take. The program is sleeping for 10 seconds, or roughly 30,000,000,000 cycles. So you'd expect to see 100% * 2 * 15,000 / 30,000,000,000 = 0.0001% cpu load. Yup, that looks like 0. – Hans Passant Dec 09 '21 at 20:18

1 Answers1

2

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.

Slugart
  • 4,535
  • 24
  • 32