C# 从ConcurrentDictionary调用异步回调-潜在死锁?
我有一个并发字典,用于管理订阅(并与其他代码链接),为了本例的目的,显示了一个简化版本 当添加或删除主题时,我需要更新外部消息总线,而不是添加额外的同步机制,并围绕字典添加和消息主题添加订阅包装一个阻塞部分,我正在使用并发字典,并在成功添加项目时回调一个操作:C# 从ConcurrentDictionary调用异步回调-潜在死锁?,c#,concurrency,async-await,C#,Concurrency,Async Await,我有一个并发字典,用于管理订阅(并与其他代码链接),为了本例的目的,显示了一个简化版本 当添加或删除主题时,我需要更新外部消息总线,而不是添加额外的同步机制,并围绕字典添加和消息主题添加订阅包装一个阻塞部分,我正在使用并发字典,并在成功添加项目时回调一个操作: class SubscriptionManager { readonly ConcurrentDictionary<string, SubscribedTypes> subscriptions = new Concurre
class SubscriptionManager
{
readonly ConcurrentDictionary<string, SubscribedTypes> subscriptions = new ConcurrentDictionary<string, SubscribedTypes>();
public void AddDynamic(string topic, Type type, Action<string> dynamicCallback)
{
subscriptions
.AddOrUpdate(
topic,
(topic) =>
{
var subscribedTypes = new SubscribedTypes(qos);
/* do some work and checks */
dynamicCallback(topic); // call back the calling code
return subscribedTypes;
},
(topic, subscribedTypes) =>
{
/* snip */
subscribedTypes.Add(type);
return subscribedTypes;
});
}
}
原始问题的更新
匿名lambda是否作为异步函数调用调用回答-这不是一个好主意。可以配置消息总线,因此可以通过IoC生命周期管理singleton或在消息总线内实现SubscriptionManager实例
在这里,性能是至关重要的,因为在review上,一些应用程序将动态创建大量订阅,并且词典将被持续读取
因此,我设置了ReaderWriterLockSlim锁,以最大限度地减少读取时的锁定(写入时升级为完全锁定),并且当需要订阅消息总线时,它在关键部分之外完成
public class SubscriptionManager
{
// SubscribedTypes is a dictionary with some additional features
private readonly Dictionary<string, SubscribedTypes> dict = new Dictionary<string, SubscribedTypes>();
private readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
private readonly IMessageBus messageBus;
SubscriptionManager(IMessageBus messageBus)
{
this.messageBus = messageBus;
}
public async Task AddDynamicAsync(string topic, Type type)
{
var subscriptionRequired = false;
WriteLock(() => {
if(this.dict.TryGetValue(topic, var out SubscribedTypes value))
{
// just add - no need to subscribe, is already subscribed when created for first time
value.DynamicRegistrations.Add(handlerType);
}
else
{
// this topic is new, add it
var subscribedTypes = new SubscribedTypes(qos);
subscribedTypes.DynamicRegistrations.Add(handlerType);
this.dict[topic] = subscribedTypes;
// we will need to register this on external bus
subscriptionRequired = true;
}
});
if(subscriptionRequired)
await this.messageBus.Subscribe(topic);
}
public async Task RemoveDynamicAsync(string topic, Type type)
{
var unsubscribeRequired = false;
WriteLock(() => {
if(this.dict.TryGetValue(topic, var out SubscribedTypes value))
{
value.DynamicRegistrations.Remove(handlerType);
unsubscribeRequired = true;
}
else
{
throw new InvalidOperationException($"topic '{topic}' not registered.");
}
});
if(unsubscribeRequired)
await this.messageBus.Unsubscribe(topic);
}
public IEnumerable<Type> GetTypesForTopic(string topic)
{
var found = ReadLock(() => {
if(this.dict.TryGetValue(topic, var out SubscribedTypes value))
return value.GetAll();
else
return new List<Type>();
});
}
private SubscribedTypes ReadLock(Func<SubscribedTypes> func)
{
rwLock.EnterReadLock();
try { return func(); }
finally { rwLock.ExitReadLock(); }
}
private void WriteLock(Action action)
{
rwLock.EnterWriteLock();
try { action(); }
finally { rwLock.ExitWriteLock(); }
}
}
公共类订阅管理器
{
//SubscribedTypes是一个具有一些附加功能的字典
专用只读词典dict=新词典();
private ReaderWriterLockSlim rBlock=new ReaderWriterLockSlim();
专用只读IMessageBus messageBus;
订阅管理器(IMessageBus messageBus)
{
this.messageBus=messageBus;
}
公共异步任务AddDynamicAsync(字符串主题,类型)
{
var subscriptionRequired=false;
写回(()=>{
if(this.dict.TryGetValue(主题,var out SubscribedTypes值))
{
//只需添加-无需订阅,第一次创建时已订阅
value.DynamicRegistrations.Add(handlerType);
}
其他的
{
//此主题是新的,请添加它
var subscribedTypes=新的subscribedTypes(qos);
subscribedTypes.DynamicRegistrations.Add(handlerType);
this.dict[topic]=订阅类型;
//我们需要在外部总线上注册
subscriptionRequired=true;
}
});
如果(需要订阅)
等待这个.messageBus.Subscribe(主题);
}
公共异步任务RemoveDynamicAsync(字符串主题,类型)
{
var unsubscribeRequired=假;
写回(()=>{
if(this.dict.TryGetValue(主题,var out SubscribedTypes值))
{
value.DynamicRegistrations.Remove(handlerType);
unsubscribeRequired=true;
}
其他的
{
抛出新的InvalidOperationException($“未注册主题“{topic}”);
}
});
如果(需要取消订阅)
等待此消息。messageBus。取消订阅(主题);
}
公共IEnumerable GetTypesForTopic(字符串主题)
{
var found=ReadLock(()=>{
if(this.dict.TryGetValue(主题,var out SubscribedTypes值))
返回值:GetAll();
其他的
返回新列表();
});
}
私有SubscribedTypes读锁(Func Func)
{
rwLock.EnterReadLock();
尝试{return func();}
最后{rwLock.exitradlock();}
}
私有无效写回(操作)
{
rBlock.EnterWriteLock();
试试{action();}
最后{rwLock.ExitWriteLock();}
}
}
您使用ConcurrentDictionary的第一种方法是有问题的,因为根据:
如果在不同的线程上同时调用AddOrUpdate
,addValueFactory
可能会被多次调用,但它的键/值对可能不会为每次调用添加到字典中
[…]在锁外部调用addValueFactory
和updateValueFactory
委托,以避免在锁下执行未知代码时可能出现的问题。因此,AddOrUpdate
对于ConcurrentDictionary
类上的所有其他操作不是原子的
您使用ConcurrentDictionary
的方式表明,您希望对AddOrUpdate
的每次调用最多执行一次提供的lambda,但这并不保证
使用普通字典
+锁
的第二种方法将无法编译,因为。您必须移动wait messageBus.Subscribe(主题)
在受保护区域之外,或者使用异步节流器,如。使用ConcurrentDictionary
的第一种方法是有问题的,因为根据:
如果在不同的线程上同时调用AddOrUpdate
,addValueFactory
可能会被多次调用,但它的键/值对可能不会为每次调用添加到字典中
[…]在锁外部调用addValueFactory
和updateValueFactory
委托,以避免在锁下执行未知代码时可能出现的问题。因此,AddOrUpdate
对于ConcurrentDictionary
类上的所有其他操作不是原子的
您使用ConcurrentDictionary
的方式表明,您希望对AddOrUpdate
的每次调用最多执行一次提供的lambda,但这并不保证
使用普通字典
+锁
的第二种方法将无法编译,因为
public class SubscriptionManager
{
// SubscribedTypes is a dictionary with some additional features
private readonly Dictionary<string, SubscribedTypes> dict = new Dictionary<string, SubscribedTypes>();
private readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
private readonly IMessageBus messageBus;
SubscriptionManager(IMessageBus messageBus)
{
this.messageBus = messageBus;
}
public async Task AddDynamicAsync(string topic, Type type)
{
var subscriptionRequired = false;
WriteLock(() => {
if(this.dict.TryGetValue(topic, var out SubscribedTypes value))
{
// just add - no need to subscribe, is already subscribed when created for first time
value.DynamicRegistrations.Add(handlerType);
}
else
{
// this topic is new, add it
var subscribedTypes = new SubscribedTypes(qos);
subscribedTypes.DynamicRegistrations.Add(handlerType);
this.dict[topic] = subscribedTypes;
// we will need to register this on external bus
subscriptionRequired = true;
}
});
if(subscriptionRequired)
await this.messageBus.Subscribe(topic);
}
public async Task RemoveDynamicAsync(string topic, Type type)
{
var unsubscribeRequired = false;
WriteLock(() => {
if(this.dict.TryGetValue(topic, var out SubscribedTypes value))
{
value.DynamicRegistrations.Remove(handlerType);
unsubscribeRequired = true;
}
else
{
throw new InvalidOperationException($"topic '{topic}' not registered.");
}
});
if(unsubscribeRequired)
await this.messageBus.Unsubscribe(topic);
}
public IEnumerable<Type> GetTypesForTopic(string topic)
{
var found = ReadLock(() => {
if(this.dict.TryGetValue(topic, var out SubscribedTypes value))
return value.GetAll();
else
return new List<Type>();
});
}
private SubscribedTypes ReadLock(Func<SubscribedTypes> func)
{
rwLock.EnterReadLock();
try { return func(); }
finally { rwLock.ExitReadLock(); }
}
private void WriteLock(Action action)
{
rwLock.EnterWriteLock();
try { action(); }
finally { rwLock.ExitWriteLock(); }
}
}