下面的代码是线程安全的吗?

我每隔service就需要调用一个async方法,因此我cannotforeach循环置于锁定之下.

但是,将锁下从_dictionaryImmutableList的所有值复制到锁下,退出锁,然后像往常一样迭代它们并调用async方法,这是否是线程安全的?

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();
        }
    }
}

ConcurrentDictionary替换Dictionary并从GetServices移除锁是一种 Select ,但我仍然必须将锁保留在GetOrAdd中,因为实际上我有2个集合要维护,而不仅仅是_dictionary.我想知道是否有可能用普通的Dictionary美元.

推荐答案

据我所知,是的.

由于在持有锁的同时制作了词典的副本,因此应该不会有并发读取和写入词典的风险.和concurrent reads are safe:

只要集合未被修改,一个词典TKey、TValue&>可以同时支持多个读取器

创建的副本是一个局部变量,不能被多个线程访问,因此使用它在默认情况下是线程安全的.甚至不需要使其不可变,使用常规列表也同样有效.

但这是专门为访问词典而设置的..DoStuffAsync()美元可能仍然不安全,但我认为这超出了范围.

Csharp相关问答推荐

try 在C#中启动Tunnelbear.Exec

EF Core的GbContent如何 suppress 所有CS 8618(不可为空属性)警告?

将C#字符串转换为其UTF8编码字符的十六进制表示

C# uwp中的Win11启动屏幕剪辑工作方式不同

C++/C#HostFXR通过std::tuple传递参数

自动映射程序在GroupBy之后使用项目

需要在重新启动ApplicartionPool或IIS后启动/唤醒API的帮助

Rider将.NET安装在哪里

使用可信第三方的Iext8.Net pdf签名

更改执行目录

.NET SDK包中的官方C#编译器在哪里?

NET8 MAUI并部署到真实设备上进行测试

如何在不复制或使用输出的情况下定义项目依赖

使用C#和.NET 7.0无法访问Cookie中的数据

使用可空引用类型时C#接口实现错误

Xamarin.Forms-如何创建可 Select 的显示alert 或弹出窗口?

流畅的验证--如何为属性重用规则?

仅在ASP.NETCore应用程序中的附加单独端口上公开一组终结点

用于分钟和秒验证的MudTextfield的正则表达式掩码

.NET8支持Vector512,但为什么向量不能达到512位?