簡體   English   中英

如何創建一個線程安全的二維鎖集合?

[英]c# How to create a thread safe, two dimensional lock collection?

我正在嘗試實現線程安全的二維集合,以控制對多個資源的訪問。 從我看到的問題出發,我需要在集合級別(這很簡單)和單個對象級別上執行鎖定,但不一定要同時執行鎖定,這可能會引起一些問題。

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上的鎖,因為這將大大減慢處理速度。

了解問題之后,我認為您應該執行以下操作:

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的返回值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM