如何使自定义线程安全通用列表返回C#中的整个列表?

时间:2010-02-24 23:40:02

标签: c# list thread-safety generic-list

我是一个线程菜鸟,我正在尝试在C#(.NET 3.5 SP1)中编写自定义线程安全通用列表类。我读过Why are thread safe collections so hard?。在审核了课程要求后,我认为我只需要安全地 添加 到列表中, 返回 列表。该示例显示了我想要的所有内容,除了它缺少 返回列表 方法,因此我编写了自己的公共方法,如下所示:

更新:根据给出的建议,我已经审核了我的要求,因此将课程简化为:

public sealed class ThreadSafeList<T>
{
    private readonly IList<T> list = new List<T>();
    private readonly object lockable = new object();

    public void Add(T t)
    {
        lock (lockable)
        {
            list.Add(t);
        }
    }

    public IList<T> GetSnapshot()
    {
        IList<T> result;
        lock (lockable)
        {
            result = new List<T>(list);
        }
        return result;
    }
}

4 个答案:

答案 0 :(得分:2)

同意@jrista。您需要解决一个语义问题,为什么称它为Translate()?意图是什么?

A - 当前代码 - 返回内部列表的只读包装

return new ReadOnlyCollection<T>(list);

如果原始列表被更改,如果另一个线程在列表上迭代,则仍然存在线程问题。只要你意识到这一点,这不是一个大问题。

B - 只读副本。

return new List<T>(list).AsReadOnly();

此列表没有线程问题,因为没有任何内容修改新列表。唯一的引用是ReadOnlyCollection<T>包装器。

C - 正常(可写)副本

return new List<T>(list);

返回一个新列表,调用者可以在不影响原始列表的情况下对列表执行所需操作,对原始列表的更改不会影响此列表。


如果另一个消费者抓取该列表的副本然后修改他们的副本,这是否重要?消费者是否需要查看列表的更改?你只需要一个线程安全的枚举器吗?

public IEnumerator<T> ThreadSafeEnumerator()
{
    List<T> copy;
    lock(lockable)
        copy = new List<T>(list);

    foreach (var value in copy)
        yield return value;
}

答案 1 :(得分:1)

根据我的经验,你必须在线程安全方面使用你的大脑,而不是依赖于这些解决方案。 简而言之,它取决于列表的接收者将使用它做什么。

答案 2 :(得分:1)

Translate()方法看起来正确。当你在Translate / AddRange时,使用锁定可以防止他人添加或修改你的列表。

我认为您的IsReadyOnly属性可能存在问题。在内部读取/写入属性时使用锁定。但也有一个没有锁定的公共吸气剂。可能会发生线程1调用MarkAsReadOnly而第二个线程在查看IsReadOnly时仍然可能出错。我会使用普通属性来锁定getter或使用volatile bool字段。

答案 3 :(得分:1)

您可以使用SynchronizedCollection。

http://msdn.microsoft.com/en-us/library/ms668265.aspx