我试图实现一个线程安全的二维集合来控制对多个资源的访问。问题来自我所看到的我需要在集合级别(这很简单)和单个对象级别执行锁定,但不一定同时可能导致某些问题。
public class LockStore
{
private object _syncLock = new object();
private Dictionary<string, StoredLock> _locks = new Dictionary<string, StoredLock>();
public StoredLock GetOrAdd(string id, StoredLock lockDetails)
{
if (!_locks.ContainsKey(id))
lock (_syncLock)
{
if (!_locks.ContainsKey(id))
_locks.Add(id, lockDetails);
}
return _locks[id];
}
public void Remove(string id)
{
lock (_syncLock)
{
_locks.Remove(id);
}
}
}
public class StoredLock
{
public string Id { get; set; }
public DateTime CreatedDateTime { get; set; }
}
例如,一个线程可以调用LockStore.GetOrAdd()方法,返回一个StoredLock对象。然后,我想锁定StoreLock对象以防止其他线程访问它,但不保留整个字典的锁。
var id = "someId"
var storedLock = lockStore.GetOrAdd(id, new StoredLock());
lock (storedLock)
{
//Do something
}
在某个地方完全不同的话......
lockStore.Remove(id);
在LockStore中存储的StoredLock对象上获取锁定时,LockStore不会保持锁定,因此理论上可以为该特定Id调用LockStore.Remove(),因为Remove()方法没有理解锁定在它实际包含的对象上。我不确定在这种情况下会发生什么,但我认为这不会很好!
另外,我认为这可能违反了用于锁定的对象无法公开访问的设计原则?
我真的很茫然,所以任何建议:解决问题或实际方法都会非常感激。
编辑:特别具体 - 我不想同时锁定LockStore集合和单独的StoredLock,因为这会大大减慢处理速度。
答案 0 :(得分:1)
在理解了问题之后,我认为你应该做这样的事情:
public class LockStore
{
private readonly Dictionary<string, StoredLock> m_Dictionary =
new Dictionary<string, StoredLock>();
public void UseResource(string resource_id, Action<StoredLock> action)
{
StoredLock stored_lock = null;
lock(m_Dictionary)
{
if (m_Dictionary.ContainsKey(resource_id))
{
stored_lock = m_Dictionary[resource_id];
}
else
{
stored_lock = new StoredLock
{
Id = resource_id,
CreatedDateTime = DateTime.Now
};
m_Dictionary.Add(resource_id,stored_lock);
}
}
lock(stored_lock)
{
action(stored_lock);
}
}
public bool RemoveLock(string resource_id)
{
lock (m_Dictionary)
{
if (!m_Dictionary.ContainsKey(resource_id))
return true;
var stored_lock = m_Dictionary[resource_id];
bool taken = false;
Monitor.TryEnter(stored_lock, ref taken);
if (!taken)
return false;
try
{
m_Dictionary.Remove(resource_id);
}
finally
{
Monitor.Exit(stored_lock);
}
return true;
}
}
}
此代码设计不完美但有效。你应该努力使它变得更好。
基本上,这个类允许你从线程1做这样的事情:
lockStore.UseResource("resource1", sl =>
{
//Do some processing with s1
});
从线程2中做这样的事情:
var removed = lockStore.RemoveLock("resource1");
RemoveLock
将立即返回,如果某个线程仍然持有该对象的锁定,则返回值为false
。