Need to understand the usage of SemaphoreSlim

Mou picture Mou · Nov 18, 2013 · Viewed 71.5k times · Source

Here is the code I have but I don't understand what SemaphoreSlim is doing.

async Task WorkerMainAsync()
{
    SemaphoreSlim ss = new SemaphoreSlim(10);
    List<Task> trackedTasks = new List<Task>();
    while (DoMore())
    {
        await ss.WaitAsync();
        trackedTasks.Add(Task.Run(() =>
        {
            DoPollingThenWorkAsync();
            ss.Release();
        }));
    }
    await Task.WhenAll(trackedTasks);
}

void DoPollingThenWorkAsync()
{
    var msg = Poll();
    if (msg != null)
    {
        Thread.Sleep(2000); // process the long running CPU-bound job
    }
}

What does await ss.WaitAsync(); and ss.Release(); do?

I guess that if I run 50 threads at a time then write code like SemaphoreSlim ss = new SemaphoreSlim(10); then it will be forced to run 10 active thread at time.

When one of 10 thread completes then another thread will start. If I am not right then help me to understand with sample situation.

Why is await needed along with ss.WaitAsync();? What does ss.WaitAsync(); do?

Answer

Servy picture Servy · Nov 18, 2013

i guess that if i run 50 thread at a time then code like SemaphoreSlim ss = new SemaphoreSlim(10); will force to run 10 active thread at time

That is correct; the use of the semaphore ensures that there won't be more than 10 workers doing this work at the same time.

Calling WaitAsync on the semaphore produces a task that will be completed when that thread has been given "access" to that token. await-ing that task lets the program continue execution when it is "allowed" to do so. Having an asynchronous version, rather than calling Wait, is important both to ensure that the method stays asynchronous, rather than being synchronous, as well as deals with the fact that an async method can be executing code across several threads, due to the callbacks, and so the natural thread affinity with semaphores can be a problem.

A side note: DoPollingThenWorkAsync shouldn't have the Async postfix because it's not actually asynchronous, it's synchronous. Just call it DoPollingThenWork. It will reduce confusion for the readers.