如何实现线程安全的自动清理字典?
EventBusManager
必须为每个 roomId 提供一个 EventBus
实例。EventBusManager
用法:
// When user connect to room
var eventBus = evtBusManager.GetIncrementUsage(roomId);
// When user disconnect
evtBusManager.DecrementUsage(roomId);
EventBusManager
实施:
public class EventBusManager {
readonly Dictionary<string, BusUsage> buses = [];
public EventBus GetIncrementUsage(string roomId) {
lock (buses) {
if (!buses.TryGetValue(roomId, out var busUsage)) {
busUsage = new BusUsage(new EventBus());
buses.Add(roomId, busUsage);
return busUsage.Bus;
}
busUsage.UsageCount++;
return busUsage.Bus;
}
}
public void DecrementUsage(string roomId) {
lock (buses) {
if (!buses.TryGetValue(roomId, out var busUsage))
return;
busUsage.UsageCount--;
if (busUsage.UsageCount == 0)
buses.Remove(roomId);
}
}
record BusUsage(EventBus Bus) {
public int UsageCount = 1;
};
}
实现线程安全吗?
基本上,它看起来在功能上是线程安全的,但您的代码中存在一个策略错误。您不应使用字段
buses
作为锁定对象。惯用的技术是这样的:
请注意,字段
lockObject
应始终为 private
。
public class EventBusManager {
//...
public EventBus GetIncrementUsage(string roomId) {
lock (lockObject) {
//...
return busUsage.Bus;
}
}
public void DecrementUsage(string roomId) {
lock (lockObject) {
//...
}
}
object lockObject = new();
// System.Threading.Lock lockObject = new(); // for .NET 9 and up
}
您可以在这里找到背后的一些理由。特别指出的一点是,锁对象应该是一个不用于任何其他目的的实例。
谢谢。
—SA