13

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

Snak
  • 673
  • 1
  • 5
  • 19
Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • I suppose this is off-topic, but there might be simpler/cheaper synchronization primitives than a semaphore when it has a limit of 1; e.g. a mutex (as you recognized, since that's what you call your variable!) or `lock`/`Monitor`. – stakx - no longer contributing Sep 17 '15 at 07:30
  • @stakx Yes. semaphore allows `x` to be at this critical section. Here I just used `1`. [Probably a lock would be better.](http://stackoverflow.com/questions/15447443/contention-on-a-resource-not-always-using-lock) – Royi Namir Sep 17 '15 at 07:33
  • 1
    C# : Lock internal to your class for single entry, Semaphore for multiple entry (e.g. worker thread per core), reader/writer lock for optimised performance on many read limited write scenarios, Mutex when cross process synchronisation is required. – Spence Sep 17 '15 at 07:36
  • 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. – rollsch Feb 19 '19 at 04:10

3 Answers3

25

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
    }
}
Spence
  • 28,526
  • 15
  • 68
  • 103
  • I didn't understand your edit . Can you please expalin by example ? – Royi Namir Sep 17 '15 at 09:36
  • 1
    The "Wait" command is a "try to obtain the mutex within x miliseconds". If the mutex is available, there is no wait and the command will return immediately to enter the mutex. It's better to read it as "TryEnter" with a wait of up to 2 seconds. – Spence Sep 17 '15 at 23:17
5

Your misconception is that there is an implicit "mutex zone" which is not defined by you.

The overload of Wait which you are using returns a boolean value which tells you whether or not the mutex was successfully entered.

What you are doing in your example is entering the critical zone whether or not the thread has acquired the mutex, making it redundant.

Generally, you would want to use this overload in any situation where you want to try to enter a mutex but also have a fallback strategy in case that it is not currently possible to acquire the mutex within the allotted time.

Rotem
  • 21,452
  • 6
  • 62
  • 109
2

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.

rollsch
  • 2,518
  • 4
  • 39
  • 65