Xaq*_*ron 108 c# collections properties thread-safety
我想要List<T>
一个属性的实现,可以毫无疑问地使用线程安全.
像这样的东西:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
Run Code Online (Sandbox Code Playgroud)
看来我仍然需要返回一个集合(克隆)的集合,所以如果我们在某个地方迭代集合并同时设置集合,那么就不会引发异常.
如何实现线程安全的集合属性?
Bal*_*a R 173
如果您的目标是.Net 4,System.Collections.Concurrent命名空间中有几个选项
你可以ConcurrentBag<T>
在这种情况下使用而不是List<T>
Chr*_*oph 81
即使获得最多的选票,人们通常也不能将其System.Collections.Concurrent.ConcurrentBag<T>
作为线程安全的替代品System.Collections.Generic.List<T>
(RadekStromský已经指出)没有订购.
但是有一个类System.Collections.Generic.SynchronizedCollection<T>
已经被称为.NET 3.0已经是框架的一部分了,但是它隐藏在一个人们不期望的地方,它鲜为人知,也许你从来没有偶然发现它(至少我从没干过).
SynchronizedCollection<T>
被编译成程序集System.ServiceModel.dll(它是客户端配置文件的一部分,但不是可移植类库的一部分).
希望有所帮助.
Tej*_*ejs 15
我认为制作一个示例ThreadSafeList类很容易:
public class ThreadSafeList<T> : IList<T>
{
protected List<T> _interalList = new List<T>();
// Other Elements of IList implementation
public IEnumerator<T> GetEnumerator()
{
return Clone().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return Clone().GetEnumerator();
}
protected static object _lock = new object();
public List<T> Clone()
{
List<T> newList = new List<T>();
lock (_lock)
{
_interalList.ForEach(x => newList.Add(x));
}
return newList;
}
}
Run Code Online (Sandbox Code Playgroud)
您只需在请求枚举器之前克隆列表,因此任何枚举都在处理运行时无法修改的副本.
如果您查看 T 列表(https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877)的源代码,您会注意到那里有一个类(当然是内部 - 为什么,微软,为什么?!?!)称为 T 的 SynchronizedList。我在这里复制粘贴代码:
[Serializable()]
internal class SynchronizedList : IList<T> {
private List<T> _list;
private Object _root;
internal SynchronizedList(List<T> list) {
_list = list;
_root = ((System.Collections.ICollection)list).SyncRoot;
}
public int Count {
get {
lock (_root) {
return _list.Count;
}
}
}
public bool IsReadOnly {
get {
return ((ICollection<T>)_list).IsReadOnly;
}
}
public void Add(T item) {
lock (_root) {
_list.Add(item);
}
}
public void Clear() {
lock (_root) {
_list.Clear();
}
}
public bool Contains(T item) {
lock (_root) {
return _list.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex) {
lock (_root) {
_list.CopyTo(array, arrayIndex);
}
}
public bool Remove(T item) {
lock (_root) {
return _list.Remove(item);
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
lock (_root) {
return _list.GetEnumerator();
}
}
IEnumerator<T> IEnumerable<T>.GetEnumerator() {
lock (_root) {
return ((IEnumerable<T>)_list).GetEnumerator();
}
}
public T this[int index] {
get {
lock(_root) {
return _list[index];
}
}
set {
lock(_root) {
_list[index] = value;
}
}
}
public int IndexOf(T item) {
lock (_root) {
return _list.IndexOf(item);
}
}
public void Insert(int index, T item) {
lock (_root) {
_list.Insert(index, item);
}
}
public void RemoveAt(int index) {
lock (_root) {
_list.RemoveAt(index);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我个人认为他们知道可以创建使用SemaphoreSlim的更好的实现,但没有实现。
即使接受的答案是ConcurrentBag,我不认为它在所有情况下都是真正的替换列表,因为Radek对答案的评论说:"ConcurrentBag是无序集合,因此与List不同,它不保证订购.而且你不能通过索引访问项目".
因此,如果您使用.NET 4.0或更高版本,解决方法可能是将ConcurrentDictionary与整数TKey一起用作数组索引,将TValue用作数组值.这是在Pluralsight的C#Concurrent Collections课程中替换列表的推荐方法.ConcurrentDictionary解决了上面提到的两个问题:索引访问和排序(我们不能依赖于排序,因为它的底层哈希表,但是当前的.NET实现保存了元素添加的顺序).
小智 5
C#的ArrayList
类有一个Synchronized
方法。
var threadSafeArrayList = ArrayList.Synchronized(new ArrayList());
Run Code Online (Sandbox Code Playgroud)
这将返回的任何实例的线程安全包装器IList
。所有操作都需要通过包装器执行,以确保线程安全。