我希望有一个可以在多线程下安全使用的 List<T>
属性的实现。
类似于这样:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
看起来我仍然需要返回集合的副本,这样如果我们在遍历集合的同时对集合进行设置,则不会引发异常。
如何实现一个线程安全的集合属性?
我希望有一个可以在多线程下安全使用的 List<T>
属性的实现。
类似于这样:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
看起来我仍然需要返回集合的副本,这样如果我们在遍历集合的同时对集合进行设置,则不会引发异常。
如何实现一个线程安全的集合属性?
我相信 _list.ToList()
会复制一份列表。如果需要,您也可以查询它,例如:
_list.Select("query here").ToList();
无论如何,msdn说这确实是一份副本而不仅仅是一个引用。哦,对了,正如其他人指出的那样,您需要锁定set方法。
看着原样例,人们可能会猜测意图是能够简单地用新的列表替换掉旧的列表。属性的设置器告诉了我们这一点。
微软的线程安全集合是用于安全地向集合中添加和删除项目的。但是,如果在应用程序逻辑中打算用新的集合替换旧的集合,我们可以再次猜测,列表的添加和删除功能是不需要的。
如果是这种情况,简单的答案就是使用IReadOnlyList接口:
private IReadOnlyList<T> _readOnlyList = new List<T>();
private IReadOnlyList<T> MyT
{
get { return _readOnlyList; }
set { _readOnlyList = value; }
}
如果您想要安全地枚举,基本上需要使用锁。
请参考MSDN。http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx
以下是MSDN的一部分,您可能会感兴趣:
此类型的公共静态成员(在Visual Basic中为Shared)是线程安全的。任何实例成员都不能保证是线程安全的。
列表可以支持多个读取器并发访问,只要集合没有被修改。枚举集合本质上不是一个线程安全的过程。在很少的情况下,当枚举与一个或多个写访问竞争时,确保线程安全的唯一方法是在整个枚举期间锁定集合。为了允许多个线程对集合进行读写访问,您必须实现自己的同步。
public class ConcurrentList
{
private long _i = 1;
private ConcurrentDictionary<long, T> dict = new ConcurrentDictionary<long, T>();
public int Count()
{
return dict.Count;
}
public List<T> ToList()
{
return dict.Values.ToList();
}
public T this[int i]
{
get
{
long ii = dict.Keys.ToArray()[i];
return dict[ii];
}
}
public void Remove(T item)
{
T ov;
var dicItem = dict.Where(c => c.Value.Equals(item)).FirstOrDefault();
if (dicItem.Key > 0)
{
dict.TryRemove(dicItem.Key, out ov);
}
this.CheckReset();
}
public void RemoveAt(int i)
{
long v = dict.Keys.ToArray()[i];
T ov;
dict.TryRemove(v, out ov);
this.CheckReset();
}
public void Add(T item)
{
dict.TryAdd(_i, item);
_i++;
}
public IEnumerable<T> Where(Func<T, bool> p)
{
return dict.Values.Where(p);
}
public T FirstOrDefault(Func<T, bool> p)
{
return dict.Values.Where(p).FirstOrDefault();
}
public bool Any(Func<T, bool> p)
{
return dict.Values.Where(p).Count() > 0 ? true : false;
}
public void Clear()
{
dict.Clear();
}
private void CheckReset()
{
if (dict.Count == 0)
{
this.Reset();
}
}
private void Reset()
{
_i = 1;
}
}
lock
语句来实现。(点击此处了解更多信息。)private List<T> _list;
private List<T> MyT
{
get { return _list; }
set
{
//Lock so only one thread can change the value at any given time.
lock (_list)
{
_list = value;
}
}
}
请注意,这可能不完全是您要求的 - 您可能想在代码中更深层次地锁定,但我不能假设。请查看 lock
关键字,并根据您的具体情况调整其使用。
如果需要,您可以在 _list
变量中同时在 get
和 set
块中进行 lock
,这将使读/写不能同时发生。
lock
语句。使用类似的示例,我可以争辩说我们不应该使用for循环,因为你几乎可以毫不费力地使应用程序死锁:for (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
- Josh M.lock (this)
和lock (typeof(this))
都是大忌的原因。 - Joel Mueller
IList<T>
实现(而不是List<T>
)吗? - Greg