Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does SemaphoreSlim's timeout defeat its own purpose?

The true power of semaphore is :

Limits the number of threads that can access a resource or pool of resources concurrently

That is understood and clear.

But I never got a chance to play with the overload of Wait which accepts a timeout integer, however - this seems to allow multiple threads get into the critical section although I've explicitly set semaphore not to allow more than one thread at a time:

private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);

private void Main()
{
    Task.Run(() => DelayAndIncrementAsync());
    Task.Run(() => DelayAndIncrementAsync());
}

private void DelayAndIncrementAsync()
{
    _mutex.Wait(2000);

    try
    {
        Console.WriteLine(0);
        Thread.Sleep(TimeSpan.FromSeconds(5));
        Console.WriteLine(1);
    }
    finally
    {
        _mutex.Release();
    }
}

enter image description here

The first thread is entering the mutex zone, prints "0", waits 5 seconds, meanwhile after 2 seconds the other thread ALSO enters the critical section?

Question

Isn't it defeating the whole purpose of semaphore?

What are the real life scenarios which I would use this timeout, especially when the basic rule is -

"Semaphore = Limits the number of threads that can access a resource or pool of resources concurrently

like image 575
Royi Namir Avatar asked Sep 17 '15 07:09

Royi Namir


People also ask

What does SemaphoreSlim do?

You can use it as a local semaphore only. The SemaphoreSlim class is the recommended semaphore for synchronization within a single app. A lightweight semaphore controls access to a pool of resources that is local to your application.

What is WaitAsync?

WaitAsync(CancellationToken) Asynchronously waits to enter the SemaphoreSlim, while observing a CancellationToken. WaitAsync(TimeSpan) Asynchronously waits to enter the SemaphoreSlim, using a TimeSpan to measure the time interval.


2 Answers

This will make people cringe but using the timeout (and confirming it did timeout) is a good way to log and track deadlock bugs. Sure if you wrote your program correctly you wouldn't need these, but I've personally used this for this purpose which has saved me a lot of time.

So yes it does defeat the purpose (in most cases) if you let it timeout and then hit the critical section with multiple threads. But it can be useful to log or detect a deadlock bug.

There are also use cases where you want multiple threads to access the critical section, but only in specific scenarios. Eg it would not be fatal and simply be undesirable for it occur. Eg you aren't using the semaphore to stop a cross thread crash, but rather something else.

like image 62
rollsch Avatar answered Oct 07 '22 01:10

rollsch


You need to check the return value of the wait. The Timeout based wait will try for 2 seconds to take the mutex then return. You need to check if the return value is true (i.e you have the mutex) or not.

Edit: Also keep in mind that the timeout based wait will return immediately if the semaphore is available, so you cant use this to prevent an infinite loop in the code via this technique.

private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);
void Main()
{

    Task.Run(()=>DelayAndIncrementAsync());
    Task.Run(()=>DelayAndIncrementAsync());
}
public   void  DelayAndIncrementAsync()
{
    if (_mutex.Wait(2000))
    {
        try
        {
            Console.WriteLine(0);
            Thread.Sleep(TimeSpan.FromSeconds(5));
            Console.WriteLine(1);
        }    
        finally
        {
            _mutex.Release();
        }
    } else {
        //oh noes I don't have the mutex
    }
}
like image 31
Spence Avatar answered Oct 07 '22 03:10

Spence