.Net Mutex, lock, not working on worker threads

Viewed 55

We have a function being called within a ASP.Net (Blazor) application simply within the processing of an HTTP request. It is on a Scoped, injected object though this is irrelevant to the problem at hand. Also the function is synchronous (does not have async) though it may be called from async functions.

A part of this functions code needs to be run in mutual exclusion. I thought this should be the simplest thing in the world and wrote the following code

using (Mutex mutex = new(true, "SyncObject")) { ... }

Basically I created a named Mutex, which should globally prevent more than 1 thread entering the block. To my surprise this did not work and with breakpoints I could see that multiple WorkerThreads entered the block of code.

After a lot of research I found that .Net has two namespaces 'Local' and 'Global' for synchronization object, since I was only interested in the 'Local' I did not need to make any change but out of frustration, I added it to the Global namespace and tried it but no luck.

using (Mutex mutex = new(true, "Global\\SyncObject")) { ... }

The above code did not work either and multiple threads entered the code.

I considered the possibility that the Worker threads may not be System threads and therefore the ownership if the mutex is always granted, then how to synchronize across two async methods becomes a question. Also since the function is synchronous a single thread would not be able to re-enter it until it completes.

using (Mutex mutex = new(false, "Global\\SyncObject")) { 
   mutex.WaitOne()
   ...
   mutex.ReleaseMutex()
 }

No luck.

Since the named mutex refused to work I tried creating a static mutex object as

private static Object mutex = new();

I tried using the above in a lock statement as

lock(mutex) {...}

This did not work either.

I found this to be amazingly strange. The behaviour of the sync objects indicates a single system thread, but then what can be done to sync whatever artificial threads .Net is creating and how can an artificial thread re-enter the function, this is not logical.

After digging in a bit I was able to see that these are indeed 2 threads, which makes sense and is as per expectation, but why won't the mutex / lock work?

enter image description here

1 Answers

The documentation suggests that you have a static readonly mutex object (static so that it is shared between instances)

class Test13
{
    // Create a new Mutex. The creating thread does not own the
    // Mutex.
    private static Mutex mut = new Mutex();
    private const int numIterations = 1;
    private const int numThreads = 3;

    static void Main()
    {
        // Create the threads that will use the protected resource.
        for(int i = 0; i < numThreads; i++)
        {
            Thread myThread = new Thread(new ThreadStart(MyThreadProc));
            myThread.Name = String.Format("Thread{0}", i + 1);
            myThread.Start();
        }

        // The main thread exits, but the application continues to
        // run until all foreground threads have exited.
    }

    private static void MyThreadProc()
    {
        for(int i = 0; i < numIterations; i++)
        {
            UseResource();
        }
    }

    // This method represents a resource that must be synchronized
    // so that only one thread at a time can enter.
    private static void UseResource()
    {
        // Wait until it is safe to enter.
        mut.WaitOne();

        Console.WriteLine("{0} has entered the protected area", 
            Thread.CurrentThread.Name);

        // Place code to access non-reentrant resources here.

        // Simulate some work.
        Thread.Sleep(500);

        Console.WriteLine("{0} is leaving the protected area\r\n", 
            Thread.CurrentThread.Name);
         
        // Release the Mutex.
        mut.ReleaseMutex();
    }
}
Related