1

I have some code in which I use SemaphoreSlim:

if (!string.IsNullOrEmpty(UserSettings.Token) && !isWithoutRefresh)
{

    if (UserSettings.Expires < ConvertToTimestamp(DateTime.UtcNow.AddMinutes(2)))
    {

        await _locker.LockAsync(async () =>
        {

            if (UserSettings.Expires < ConvertToTimestamp(DateTime.UtcNow.AddMinutes(2)))
            {
                if (Role.Guest.Equals(UserSettings.Role)
                    && !string.IsNullOrEmpty(UserSettings.Email))
                {
                    //TODO: initialize guest session
                }
                else
                {
                    await RefreshToken(httpClient);
                    await AlertService.ShowAsync("Odswiezony");
                }
            }
        });
    }
}

This is the code for my _locker:

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

public async Task LockAsync(Func<Task> worker)
{
    await _semaphore.WaitAsync();
    try
    {
        await worker();
    }
    finally
    {
        _semaphore.Release();
    }
}

My question is that what I can do to release all threads async after one of my threads inside the locker made some action? Because in this situation, every thread will be made synchronously. My desired solution is to have logic when I need to Refresh the token and more than 1 thread can make this action allow only the first of them to do this, and make rest of them asynchronous.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Redduke
  • 27
  • 6
  • 1
    I'm not sure if I understood you correctly but the point of a `SemaphoreSlim` is to wait asynchronously until a resource can be used again -- you're already doing that. If the first thread starts to refresh your token, the others will wait for it to finish. – devsmn Sep 23 '21 at 15:10
  • Yes that’s correct, but other threads will be executed one after another, but I want to execute them in parallel – Redduke Sep 23 '21 at 17:41
  • In that case, what's the point of locking? If you remove your semaphore handling it will work as you expect, won't it? – devsmn Sep 23 '21 at 18:39
  • Nope, because, if I delete semaphoreSlim more than one thread will refresh the token, not only one – Redduke Sep 24 '21 at 07:24
  • So you want the _first_ thread to refresh and the _others_ to do nothing but wait until the first thread is done.. and do nothing afterwards either, right? – devsmn Sep 24 '21 at 07:51
  • yes, that is correct – Redduke Sep 24 '21 at 08:28
  • This question might be relevant: [Enforce an async method to be called lazily on demand, and called again when the previous result has expired](https://stackoverflow.com/questions/68467426/enforce-an-async-method-to-be-called-lazily-on-demand-and-called-again-when-the/68478716#68478716) – Theodor Zoulias Sep 24 '21 at 18:06

1 Answers1

0

SemaphoreSlim is for mutual exclusion. It works great for limiting concurrency - in this case, to one at a time. But what you want to do is not mutual exclusion.

In this case, what you really want is an asynchronous cache that happens to contain a single item. A proper asynchronous cache does not currently exist AFAIK, but I have a partially-tested semi-proven one here.

Or you could build your own. I'd recommend starting with an asynchronous manual-reset event (either one based on Stephen Toub's code or use my AsyncEx library); then you'd need to add the logic that decides to "reset" it based on your expiration token.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810