我是一个线程菜鸟,我正在尝试在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;
}
}
答案 0 :(得分:2)
同意@jrista。您需要解决一个语义问题,为什么称它为Translate()
?意图是什么?
return new ReadOnlyCollection<T>(list);
如果原始列表被更改,如果另一个线程在列表上迭代,则仍然存在线程问题。只要你意识到这一点,这不是一个大问题。
return new List<T>(list).AsReadOnly();
此列表没有线程问题,因为没有任何内容修改新列表。唯一的引用是ReadOnlyCollection<T>
包装器。
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。