C
C#•11mo ago
Claudiu HBann

async/await and mutexes

I have a service that i will inject it as singleton that has:
c#
private readonly Dictionary<string, List<string>> _fromToAll = [];
private readonly Mutex _mutex = new();
c#
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:
c#
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();
}
}
c#
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." Even if i add ConfigureAwait(true) doesn't switch the context to the captured one. Any idea why this happens or how can I do it differently? Thank you in advance 😃
5 Replies
FestivalDelGelato
FestivalDelGelato•11mo ago
first, are you sure you need a mutex? 2nd, the sync primitive with WaitAsync is SemaphoreSlim, iirc (instead of mutex) (i mean this logic/piece of code seems a little dubious)
Claudiu HBann
Claudiu HBannOP•11mo ago
that's how I would do it in c++, a std::mutex and a std::scoped_lock, idk in c# exactly I think I do need a mutex, if 2 or more threads call at the "same" time it will populate 2 or more times with the same values I don't know if I need a mutex exactly, but I do need to sync the initialization a friend of mine said that i should make a hosted service for this, is this a good solution?
FestivalDelGelato
FestivalDelGelato•11mo ago
that's the point, if FindFromToAll is always called after an await then probably you won't need to care about this it's kind of a weak argument but still... depends what this does and how it is used 🤷
Claudiu HBann
Claudiu HBannOP•11mo ago
I want to do this as correctly as possible
Claudiu HBann
Claudiu HBannOP•11mo ago
I think what he said is an overkill, I want this to be initialized only once and when I need it not to run a service indefinetly that does nothing https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-8.0&tabs=visual-studio
Background tasks with hosted services in ASP.NET Core
Learn how to implement background tasks with hosted services in ASP.NET Core.

Did you find this page helpful?