c#如何创建线程安全的二维锁集合

本文关键字:集合 二维 线程 何创建 创建 安全 | 更新日期: 2023-09-27 18:29:39

我正在尝试实现一个线程安全的二维集合,以控制对多个资源的访问。从我所看到的问题来看,我需要在集合级别(这很简单)和单个对象级别执行锁定,但不一定同时执行这两个级别,这可能会导致一些问题。

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()方法不了解它实际包含的对象上持有的锁。我不确定在这种情况下会发生什么,但我认为这不好!

此外,我认为这可能会违反用于锁定的对象不能公开访问的设计原则?

我真的对此感到不知所措,所以任何关于解决问题或实际方法的建议都将不胜感激。

EDIT:特别具体地说,我不想同时锁定LockStore集合和单个StoredLock,因为这会大大减慢处理速度。

c#如何创建线程安全的二维锁集合

在理解了这个问题之后,我认为你应该做这样的事情:

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