2014-02-15 33 views
1

我知道,怎麼諷刺的是,我遇到了死鎖比較麻煩,而實現這個ThreadsafeCollection類比我將不得不否則......在任何情況下:僵局定製ThreadsafeCollection類

我試圖創建一個簡單的集合類,爲添加,刪除和迭代維護自己的線程同步。除了通過foreach或Linq迭代收集之外,一切似乎都很好。它使用專用ReaderWriterLockSlim,其在Add()Remove()方法中調用EnterWriteLock()/ExitWriteLock(),並在GetEnumerator()的呼叫中返回SafeEnumerator<T>SafeEnumerator<T>執行IEnumerator<T>並通過它的構造函數接收對前述ReaderWriterLockSlim的引用,以便它可以在構建過程中調用EnterReadLock(),並在Dispose()中調用ExitReadLock()

我注意到(通過體驗死鎖和驗證ExitReadLock()(因此SafeEnumerator.Dispose())不會被調用)的問題是,集合有時無法獲得writelocks,因爲一個或多個線程不會釋放他們readlocks(通過安全枚舉器)。儘管我的ReaderWRiterLockSlim是用LockRecursionPolicy.SupportsRecursion屬性構建的,但這是事實。

在我的代碼,遍歷ThreadsafeCollection唯一的地方用任何一foreach或LINQ表達這樣做,這是我的理解是獲得(安全)枚舉這種方式不要求Dispose()的顯式調用,甚至在迭代期間出現異常的情況下。

我的問題是:我誤解了foreach/LINQ/dispose一起工作的方式嗎?由於線程僅通過foreach或LINQ獲得枚舉數,因此線程無法調用SafeEnumerator.Dispose()的原因是什麼?

僅供參考,我已在下面發佈了我的ThreadsafeCollectionSafeEnumerator的相關部分。

感謝您的幫助!

ThreadsafeCollection:

public class ThreadsafeCollection<T> : IEnumerable<T> 
{ 
    private ICollection<T> _collection; 
    private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); 

    public ThreadsafeCollection() 
    { 
    _collection = new Collection<T>();  
    } 

    public void Add(T item) 
    { 
    _lock.EnterWriteLock(); 

    try 
    { 
     _collection.Add(item); 
    } 

    finally 
    { 
     _lock.ExitWriteLock(); 
    } 
    } 

    public bool Remove(T item) 
    { 
    _lock.EnterWriteLock(); 

    try 
    { 
     return _collection.Remove(item); 
    } 
    finally 
    { 
     _lock.ExitWriteLock(); 
    } 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
    return new SafeEnumerator<T>(_collection.GetEnumerator(), _lock); 
    } 
} 

SafeEnumerator:

public class SafeEnumerator<T> : IEnumerator<T> 
    { 
    private readonly IEnumerator<T> _inner; 
    private readonly ReaderWriterLockSlim _lock; 

    public SafeEnumerator(IEnumerator<T> inner, ReaderWriterLockSlim collectionlock) 
    { 
     _inner = inner; 
     _lock = collectionlock; 
     _lock.EnterReadLock(); 
    } 

    public void Dispose() 
    { 
     _lock.ExitReadLock(); 
    } 

    public bool MoveNext() 
    { 
     return _inner.MoveNext(); 
    } 

    public void Reset() 
    { 
     _inner.Reset(); 
    } 

    public T Current 
    { 
     get { return _inner.Current; } 
    } 

    object IEnumerator.Current 
    { 
     get { return Current; } 
    } 
    } 
+0

您已經驗證您的枚舉總是列舉結束了嗎?如果它們停在中間(或者在枚舉的同一個線程的中間發生寫操作),那麼這將是一個簡單的死鎖方法。避免這種情況的一種方法是返回枚舉器,該枚舉器實際上在請求枚舉時包裝集合的快照。也許不適合你的目的,但顯然不太容易陷入僵局。 – dlev

+0

我已驗證我的所有枚舉都已完成,這就是爲什麼這很令人困惑。我已決定使用替代解決方案,所以我放棄了試圖找出原因! – Tsherr

回答

1

你創建自己的實施辦法另一種方法是使用內建在.NET中的線程安全的集合。我不確定你使用的是哪個版本的.NET,但是從.NET 4開始,引入了一些線程安全的集合,可以防止創建自己的頭痛。

ConcurrentBag <T>集合是可能適合您需要的無序對象列表的線程安全實現。

這些集合的名單可以在這裏找到:http://msdn.microsoft.com/en-us/library/dd997305%28v=vs.100%29.aspx

+0

除了ConcurrentBag 沒有實現ICollection ,並不適合,因爲它不支持(某些)類似集合的操作。不過,我注意到了System.Collections.Generic中的SynchronizedCollection 類...我之前沒有注意到這一點!它做我需要的,所以我使用那個。儘管你的回答很有洞察力,但我不能將其標記爲正確,因爲我還沒有確定我原來的實施中的錯誤是什麼。 – Tsherr