Pip*_*ppo 2 c# multithreading thread-safety syncroot blazor
我在 Blazor 服务器应用程序的范围服务中有 2 个字典,我用它来管理多租户的状态。我注意到用户在不同线程上修改字典可能存在并发问题。
public Dictionary<string, GlobalDataModel> Global { get; } = new();
public Dictionary<string, Dictionary<long, LocalDataModel>> Local { get; } = new();
Run Code Online (Sandbox Code Playgroud)
我想我倾向于将它们保留为普通字典并在修改或迭代时锁定同步根。
如果我要将一个项目添加到包含类中的集合中,它会是:
if (Local.ContainsKey(tenantId) == false)
{
lock (syncRoot)
{
Local.Add(tenantId, new Dictionary<long, LocalDataModel>());
}
}
Run Code Online (Sandbox Code Playgroud)
或者:
lock (syncRoot)
{
if (Local.ContainsKey(tenantId) == false)
{
{
Local.Add(tenantId, new Dictionary<long, LocalDataModel>());
}
}
}
Run Code Online (Sandbox Code Playgroud)
那么,如果我要将服务集合的不同部分从外部类复制到列表中,以便可以安全地迭代它,我是否只锁定在服务级别、字典级别或数据模型级别,或者它是依赖的?
假设我想要一个特定项目中的资源集合。可不可能是:
[Inject] private IDataAdaptor DataAdaptor { get; set; };
var resources;
lock (DataAdaptor)
{
resources = DataAdaptor.Local[TenantId][ProjectId].Resources;
}
Run Code Online (Sandbox Code Playgroud)
或者:
lock (DataAdaptor.Local[TenantId][ProjectId].Resources)
{
resources = DataAdaptor.Local[TenantId][ProjectId].Resources;
}
Run Code Online (Sandbox Code Playgroud)
对于集合的不同部分、租户、项目等,反之亦然......
我假设我必须锁定该对象,因为syncRoot在包含的类之外无法访问该对象,或者我是否在SyncRoot要复制并锁定该对象的类中创建一个对象?
显然多线程是一个新概念。
仅当由多个读取器和零写入器使用时才是线程Dictionary<TKey,TValue> 安全的。一旦你在混合中添加了一个作家,换句话说,如果字典没有被冻结并且可以改变,那么它就毫无意义了。每次对字典的访问,即使是最轻微的接触(例如阅读它)Count,都应该同步,否则它的行为是未定义的。
同步字典的效率可能非常低,尤其是当您必须枚举它并对其每个条目执行某些操作时。在这种情况下,您必须创建字典的副本 ( .ToArray()) 并枚举该副本,或者lock在整个迭代期间阻塞所有其他可能想要进行简单读取或写入的线程。更适合多线程环境的替代集合是ConcurrentDictionary<TKey,TValue>和ImmutableDictionary<TKey,TValue>。
如果您想系统地了解线程安全性,这里有一个在线资源: Joseph Albahari 的Threading in C# - Thread Safety。