Is the below code thread-safe?
I need to call an async
method on every service
, therefore I cannot keep the foreach
loop under the lock.
But would it be thread-safe to copy all the values from the _dictionary
to an ImmutableList
under the lock, exit the lock and then iterate over them as usual and call the async
method?
public class Cache
{
private readonly ReaderWriterLockSlim lockObject = new();
private readonly IDictionary<string, Service> _dictionary = new Dictionary<string, Service>();
public Service GetOrAdd(string key)
{
lockObject.EnterUpgradeableReadLock();
try
{
if (_dictionary.TryGetValue(key, out var service))
{
return service;
}
lockObject.EnterWriteLock();
try
{
var value = new Service();
_dictionary.Add(key, value);
return service;
}
finally
{
lockObject.ExitWriteLock();
}
}
finally
{
lockObject.ExitUpgradeableReadLock();
}
}
public async Task CallServices()
{
var services = GetServices();
foreach (var service in services)
{
await service.DoStuffAsync();
}
}
private ImmutableList<Service> GetServices()
{
lockObject.EnterReadLock();
try
{
return _dictionary.Values.ToImmutableList();
}
finally
{
lockObject.ExitReadLock();
}
}
}
Replacing the Dictionary
with the ConcurrentDictionary
and removing the lock from the GetServices
is one option, but I still have to keep the lock in GetOrAdd
because in realilty I have 2 collections to maintain, not only the _dictionary
. I was wondering if it was possible to use a normal Dictionary
.