簡體   English   中英

需要改進的線程鎖定建議

[英]Improved Thread locking advice needed

我有以下場景:

我正在嘗試鎖定一個線程,如果該線程的'custom'id與已經進入已鎖定部分的代碼匹配,但是如果id不同則不匹配。

我創建了一些示例代碼來解釋我想要的行為

class A
{        
    private static Dictionary<int, object> _idLocks = new Dictionary<int, object>();
    private static readonly object _DictionaryLock = new object();
    private int _id;

    private void A (int id)
    {
        _id = id;
    }

    private object getObject()
    {
        lock (_DictionaryLock)
        {
            if (!_idLocks.ContainsKey(_id))
                _idLocks.Add(_id, new object());
        }
        lock (_idLocks[_id])
        {
            if (TestObject.Exists(_id))
                return TestObject(_id);
            else
                return CreateTestObject(_id);
        }
    }
}

現在這對於我擴展的內容100%工作,其中id示例1不檢查其對象是否已創建,而另一個ID為1的線程已經忙於創建該對象。

但是擁有兩個鎖和一個靜態字典似乎並不是正確的做法,所以我希望有人能告訴我一個改進的方法來阻止線程訪問代碼只有當該線程創建時具有與一個人已經忙着執行鎖定部分的代碼。

我正在查看ReaderWriterLockSlim類,但對我來說,使用它並沒有多大意義因為我不希望在它仍然被創建的同時讀取對象TestObject(id)。

我不關心鎖定線程訪問字典。 我不惜一切代價避免的是該線程運行的_id不應該在CreateTestObject(_id)而已經有一個忙,因為正在創建文件並使用該id刪除,如果兩個線程將拋出異常正在嘗試訪問相同的文件

只需一個普通的鎖即可修復,但在這種情況下,我仍然需要一個當前沒有在CreateTestObject(_id)方法中運行_id的線程,以便能夠在鎖內輸入代碼。

這就是因為CreateTestObject內部發生的事情需要時間,如果線程正在等待訪問它,性能將受到影響。

看起來您正在使用此代碼以線程安全的方式填充字典 - 您是否可以使用ConcurrentDictionary

class A {
  private static ConcurrentDictionary<int, object> _dictionary = new ConcurrentDictionary<int, object>();

  private int _id;

  private object GetObject() {
    object output = null;
    if(_dictionary.TryGetValue(_id, output)) {
      return output;
    } else {
      return _dictionary.GetOrAdd(_id, CreateTestObject(_id));
    }
  }
}

編輯:如果你想完全消除調用重復的CreateTestObject方法的可能性,那么你可以在_dictionary中存儲一個包裝器, _dictionary可以懶惰地設置object

class Wrapper {
  private volatile object _obj = null;

  public object GetObj() {
    while(_obj == null) {
      // spin, or sleep, or whatever
    }
    return _obj;
  }

  public void SetObj(object obj) {
    _obj = obj;
  } 
}

class A {
  private static ConcurrentDictionary<int, Wrapper> _dictionary = new ConcurrentDictionary<int, Wrapper>();

  private int _id;

  private object GetObject() {
    Wrapper wrapper = null;
    if(_dictionary.TryGetValue(_id, wrapper)) {
      return wrapper.GetObj();
    } else {
      Wrapper newWrapper = new Wrapper();
      wrapper = _dictionary.GetOrAdd(_id, newWrapper);
      if(wrapper == newWrapper) {
        wrapper.SetObj(CreateTestObject(_id));
      }
      return wrapper.GetObj();
    }
  }
}

只有一個線程能夠在指定的_id中的_dictionary中放置一個新的Wrapper - 該線程將初始化wrapper == newWrapper內的對象wrapper == newWrapper條件。 Wrapper#GetObj旋轉直到設置對象,這可以改為阻塞。

這不起作用,因為 Monitor (由 lock語句在內部使用)是可重入的。 這意味着一個線程可以輸入它已經擁有的任何鎖的任何次數。

\n

您可以通過使用 Semaphore而不是 Monitor來解決這個問題,但請暫停一段時間並聽取您的要求 - 您希望線程阻止該同一線程擁有的 lock 如何線程 曾經打算醒來? 它將永遠死鎖 - 等待 lock被釋放,同時也是持有 lock

或者你只是試圖處理一些對象的延遲初始化而不必阻塞所有其他線程? 這其實很簡單:

ConcurrentDictionary<int, YourObject> dictionary;

return dictionary.GetOrAdd(id, i => CreateTestObject(i));

請注意, 只有在字典中不存在鍵時才會調用CreateTextObject

暫無
暫無

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

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