I have a service that i will inject it as singleton that has:
private readonly Dictionary<string, List<string>> _fromToAll = [];
private readonly Mutex _mutex = new();
and I want the dictionary to be lazy initialized only once when needed like:
private async Task<Dictionary<string, List<string>>> FindFromToAll()
{
try
{
_mutex.WaitOne();
if (_fromToAll.Count > 0)
{
return _fromToAll;
}
foreach (var serviceType in FindAllServiceTypes())
{
var service = ActivatorUtilities.CreateInstance(provider, serviceType);
var fromTo = await service.Invoke<Task<List<string>>>("FromTo");
_fromToAll.Add(serviceType.Name.Replace("Service", null), fromTo);
}
return _fromToAll;
}
finally
{
_mutex.ReleaseMutex();
}
}
but when I invoke and wait for the FromTo Task that returns a list of strings the context changes and when i release the mutex it crashes because i release it from an another thread, the exception message looks like this: "Object synchronization method was called from an unsynchronized block of code."
Any idea why this happens or how can I do it differently? Thank you in advance
Even if i add ConfigureAwait(true) doesn't switch the context to the captured one.
Mutexes have thread affinity, which means that whenever you lock it on one thread, that same thread has to unlock it. When you
awaitsomething without a synchronisation context, the continuation goes on the thread pool, and so, odds are very high that the thread running it (and thus the unlock) is different from the one that initially locked it.The solution is to use a different primitive. The easiest approach is to use
SemaphoreSlim, which is async-aware, with an initial count of 1:And then, your code becomes:
Just like
Mutex,SemaphoreSlimis disposable, so make sure you dispose of it when you're done.As an added bonus, you can pass a
CancellationTokentoWaitAsync()if you want a cancellable wait.