Thomas
Thomas

Reputation: 34188

Regarding the usage of SemaphoreSlim with Async/Await

I am not an advanced developer. I'm just trying to get a hold on the task library and just googling. I've never used the class SemaphoreSlim so I would like to know what it does. Here I present code where SemaphoreSlim is used with async & await but which I do not understand. Could someone help me to understand the code below.

1st set of code

await WorkerMainAsync();

async Task WorkerMainAsync()
{
    SemaphoreSlim ss = new SemaphoreSlim(10);
    while (true)
    {
        await ss.WaitAsync();
        // you should probably store this task somewhere and then await it
        var task = DoPollingThenWorkAsync();
    }
}

async Task DoPollingThenWorkAsync(SemaphoreSlim semaphore)
{
    var msg = Poll();
    if (msg != null)
    {
        await Task.Delay(3000); // process the I/O-bound job
    }

    // this assumes you don't have to worry about exceptions
    // otherwise consider try-finally
    semaphore.Release();
}

Firstly, the WorkerMainAsync will be called and a SemaphoreSlim is used. Why is 10 passed to the constructor of SemaphoreSlim?

When does the control come out of the while loop again?

What does ss.WaitAsync(); do?

The DoPollingThenWorkAsync() function is expecting a SemaphoreSlim but is not passed anything when it is called. Is this typo?

Why is await Task.Delay(3000); used?

They could simply use Task.Delay(3000) but why do they use await here instead?

2nd set of code for same purpose

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
    }
}

Here is a task & ss.Release added to a list. I really do not understand how tasks can run after adding to a list?

trackedTasks.Add(Task.Run(async () => 
{
    await DoPollingThenWorkAsync();
    ss.Release();
}));

I am looking forward for a good explanation & help to understand the two sets of code. Thanks

Upvotes: 12

Views: 26844

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456507

why 10 is passing to SemaphoreSlim constructor.

They are using SemaphoreSlim to limit to 10 tasks at a time. The semaphore is "taken" before each task is started, and each task "releases" it when it finishes. For more about semaphores, see MSDN.

they can use simply Task.Delay(3000) but why they use await here.

Task.Delay creates a task that completes after the specified time interval and returns it. Like most Task-returning methods, Task.Delay returns immediately; it is the returned Task that has the delay. So if the code did not await it, there would be no delay.

just really do not understand after adding task to list how they can run?

In the Task-based Asynchronous Pattern, Task objects are returned "hot". This means they're already running by the time they're returned. The await Task.WhenAll at the end is waiting for them all to complete.

Upvotes: 18

Related Questions