The nature of C# locking issues explained

Sometimes in the life of a programmer everything seems peachy, C#/DotNet provide relatively easy multi threading capabilities which 99.9% of the time work just fine as explained everywhere online.

However when things get a bit serious as these online explanations tend to be off by a small bit, only that small bit creates havoc….

Imagine having to debug code within a lock() statement that crashes with exceptions like “Collection was modified; enumeration operation may not execute” which seemingly bypass any locking in place.
These are not fun to deal with as everyone will for some reason stubbornly tell you that that is not possible and you are simply lacking a lock somewhere and just moving on like they have just shed divine light.

First of all lets get the common misconception cleared that it is impossible for a single thread to execute code in parallel and even worse that events will always be fired with a new thread affinity. This is just plain wrong!

Create a simple empty form with the following code in the shown event:

            Button testButton = new Button { Dock = DockStyle.Fill };
            testButton.Click += Test_Click;
            Controls.Add(testButton);

And paste the following somewhere below:

        private readonly object _lockHelper = new object();
        private void Test_Click(object sender, EventArgs e)
        {
            lock(_lockHelper)
            {
                Debug.WriteLine("A");

                int i = 0;
                while (i++ < 50)
                {
                    Application.DoEvents();
                    Thread.Sleep(50);
                }

                Debug.WriteLine("B");
            }
        }

If what everybody proclaims is right this code should just lock up the UI and neatly print "A", "B" in sequence when triggered.
This is where the bad news starts, it doesn't, simply clicking the button a few times very easily prints "A", "A", "B", "B".
So with a few lines of code we have disproved two very stubborn myths in one blow.

Now for a somewhat more sensible approach:

        private static readonly SemaphoreSlim _lockHelper = new SemaphoreSlim(1, 1);
        private void Test_Click(object sender, EventArgs e)
        {
            _lockHelper.Wait();
            Debug.WriteLine("A");

            int i = 0;
            while (i++ < 50)
            {
                Application.DoEvents();
                Thread.Sleep(50);
            }

            Debug.WriteLine("B");
            _lockHelper.Release();
        }

This properly locks up the UI as expected (it's example code, not release code) and can even be used for some more interesting purposes when multi threading.

Please don't be like all the other assholes online who proclaim that this doesn't happen in normal production code (or even worse that a simple lock() should always be sufficient as events have different thread affinities).
Simply imagine a simple asynchronous communication protocol between two programs or a server and a client, having simple locks can seriously fuck up your day with some long debugging ahead.