Is it possible to get a count of queued threads waiting with Monitor.Enter?

Viewed 156

I'm running some Singleton code that is not thread safe, and takes some time to run. It can occasionally be called simultaneously by multiple users, so I'm using Monitor to handle the processing request queuing as below;

bool lockWasTaken = false;
try
{
    Monitor.TryEnter(lockObject, ref lockWasTaken); // returns lockWasTaken = true if it can get a lock
    if (!lockWasTaken)
    {
        log.Warn("Locked by existing request. Request is queued.");
        Monitor.Enter(lockObject, ref lockWasTaken); // Goes into the queue to access the object
    }

    // Do the Singleton processing

}
catch(Exception ex)
{
    log.Fatal(ex);
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(lockObject);
    }
}

This all works well. But what I would like to do is to be able to log how many queued requests there are. Is this possible?

1 Answers

Thanks for @Theodor Zoulias for pointing me in the right direction for the solution below using the Interlocked class;

private static readonly Object lockObject = new Object(); // just a random object used to decide whether the thread has locked the Singleton functions
private static int queueCount = 0; // tracked across multiple threads

bool lockWasTaken = false;
try
{
    // Increments the queueCount variable across multiple threads
    // https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.increment?view=netframework-4.8
    int currentQueueCount = Interlocked.Increment(ref queueCount);

    Monitor.TryEnter(lockObject, ref lockWasTaken); // returns lockWasTaken = true if it can get a lock
    if (!lockWasTaken)
    {
        log.Warn("Locked by existing request. Request is queued. Queue length is " + (currentQueueCount - 1).ToString()); // subtract since the first request is already processing
        Monitor.Enter(lockObject, ref lockWasTaken); // Goes into the queue to access the object
    }

    // Do the Singleton processing

}
catch(Exception ex)
{
    log.Fatal(ex);
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(lockObject);
    }
    // Reduce the queue count
    Interlocked.Decrement(ref queueCount);
}
Related