5

我是一个线程新手,我正在尝试用 C# (.NET 3.5 SP1) 编写一个自定义线程安全的通用列表类。我读过为什么线程安全的集合这么难?. 在查看了课程的要求后,我认为我只需要安全地添加到列表并返回列表即可。该示例几乎显示了我想要的所有内容,只是它缺少返回列表方法,因此我编写了自己的公共方法,如下所示:

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

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

4 回答 4

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;
}
于 2010-02-25T04:08:10.430 回答
1

以我的经验,当涉及到线程安全时,你必须动脑筋,而不是依赖诸如此类的解决方案。简而言之,这取决于列表的接收者将如何处理它。

于 2010-02-24T23:46:32.670 回答
1

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

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

于 2010-02-24T23:53:09.863 回答
1

您可以使用 SynchronizedCollection。

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

于 2012-08-30T17:29:05.557 回答