C# C TPL多个列表枚举器一个修饰符,无饥饿
我有几个通过TPL创建的线程可能不相关。一些线程通过简单地枚举包含的列表而不修改任何内容来与特定对象交互。其他线程从列表中添加或删除项目。目前,我在枚举列表的所有代码段周围都有lock语句,在修改列表的所有代码段周围都有lock语句 我没有遇到任何性能问题,仅此而已。然而,我确实意识到更有效的解决方案是允许多个并发枚举数,并且在修改列表时只锁定其他所有内容。目前,在任何给定时间,只有一个线程可以枚举列表。作为将来的参考,什么样的模式允许这样做 重要的。有很多解决方案在很多情况下都能很好地工作,但很可能对我的不起作用。在我的应用程序中,有一个很好的机会,大量的读者将永远不会停止——因此,所有的修改器都将被饿死。我正在寻找:C# C TPL多个列表枚举器一个修饰符,无饥饿,c#,locking,task-parallel-library,C#,Locking,Task Parallel Library,我有几个通过TPL创建的线程可能不相关。一些线程通过简单地枚举包含的列表而不修改任何内容来与特定对象交互。其他线程从列表中添加或删除项目。目前,我在枚举列表的所有代码段周围都有lock语句,在修改列表的所有代码段周围都有lock语句 我没有遇到任何性能问题,仅此而已。然而,我确实意识到更有效的解决方案是允许多个并发枚举数,并且在修改列表时只锁定其他所有内容。目前,在任何给定时间,只有一个线程可以枚举列表。作为将来的参考,什么样的模式允许这样做 重要的。有很多解决方案在很多情况下都能很好地工作,但
Enumerate 1
Enumerate 2 Concurrent with 1
Modify 1 Request is Queued
Enumerate 3 Request is Queued because of Modify Request
Enumerate 4 Request is Queued
Modify 2 Request is Queued
Enumerate 2 Finishes
Enumerate 1 Finishes
Modify 1 Starts because all in-progress at time of request Enumerators Finished
Modify 1 Finishes
Enumerate 3 Starts because Queued Modify 1 Finished
Enumerate 4 Starts
Enumerate 3 Finishes
Enumerate 4 Finishes
Modify 2 Starts
...
我最近不得不实现一个并发列表,如果有帮助,欢迎您尝试:
public class ConcurrentList<T> : IList<T>, IList
{
private readonly List<T> underlyingList = new List<T>();
private readonly object syncRoot = new object();
private readonly ConcurrentQueue<T> underlyingQueue;
private bool requiresSync;
private bool isDirty;
public ConcurrentList()
{
underlyingQueue = new ConcurrentQueue<T>();
}
public ConcurrentList(IEnumerable<T> items)
{
underlyingQueue = new ConcurrentQueue<T>(items);
}
private void UpdateLists()
{
if (!isDirty)
return;
lock (syncRoot)
{
requiresSync = true;
T temp;
while (underlyingQueue.TryDequeue(out temp))
underlyingList.Add(temp);
requiresSync = false;
}
}
public IEnumerator<T> GetEnumerator()
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.ToList().GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T item)
{
if (requiresSync)
lock (syncRoot)
underlyingQueue.Enqueue(item);
else
underlyingQueue.Enqueue(item);
isDirty = true;
}
public int Add(object value)
{
if (requiresSync)
lock (syncRoot)
underlyingQueue.Enqueue((T)value);
else
underlyingQueue.Enqueue((T)value);
isDirty = true;
lock (syncRoot)
{
UpdateLists();
return underlyingList.IndexOf((T)value);
}
}
public bool Contains(object value)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.Contains((T)value);
}
}
public int IndexOf(object value)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.IndexOf((T)value);
}
}
public void Insert(int index, object value)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.Insert(index, (T)value);
}
}
public void Remove(object value)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.Remove((T)value);
}
}
public void RemoveAt(int index)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.RemoveAt(index);
}
}
T IList<T>.this[int index]
{
get
{
lock (syncRoot)
{
UpdateLists();
return underlyingList[index];
}
}
set
{
lock (syncRoot)
{
UpdateLists();
underlyingList[index] = value;
}
}
}
object IList.this[int index]
{
get { return ((IList<T>)this)[index]; }
set { ((IList<T>)this)[index] = (T)value; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool IsFixedSize
{
get { return false; }
}
public void Clear()
{
lock (syncRoot)
{
UpdateLists();
underlyingList.Clear();
}
}
public bool Contains(T item)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.CopyTo(array, arrayIndex);
}
}
public bool Remove(T item)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.Remove(item);
}
}
public void CopyTo(Array array, int index)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.CopyTo((T[])array, index);
}
}
public int Count
{
get
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.Count;
}
}
}
public object SyncRoot
{
get { return syncRoot; }
}
public bool IsSynchronized
{
get { return true; }
}
public int IndexOf(T item)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.IndexOf(item);
}
}
public void Insert(int index, T item)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.Insert(index, item);
}
}
}
我最近不得不实现一个并发列表,如果有帮助,欢迎您尝试:
public class ConcurrentList<T> : IList<T>, IList
{
private readonly List<T> underlyingList = new List<T>();
private readonly object syncRoot = new object();
private readonly ConcurrentQueue<T> underlyingQueue;
private bool requiresSync;
private bool isDirty;
public ConcurrentList()
{
underlyingQueue = new ConcurrentQueue<T>();
}
public ConcurrentList(IEnumerable<T> items)
{
underlyingQueue = new ConcurrentQueue<T>(items);
}
private void UpdateLists()
{
if (!isDirty)
return;
lock (syncRoot)
{
requiresSync = true;
T temp;
while (underlyingQueue.TryDequeue(out temp))
underlyingList.Add(temp);
requiresSync = false;
}
}
public IEnumerator<T> GetEnumerator()
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.ToList().GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T item)
{
if (requiresSync)
lock (syncRoot)
underlyingQueue.Enqueue(item);
else
underlyingQueue.Enqueue(item);
isDirty = true;
}
public int Add(object value)
{
if (requiresSync)
lock (syncRoot)
underlyingQueue.Enqueue((T)value);
else
underlyingQueue.Enqueue((T)value);
isDirty = true;
lock (syncRoot)
{
UpdateLists();
return underlyingList.IndexOf((T)value);
}
}
public bool Contains(object value)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.Contains((T)value);
}
}
public int IndexOf(object value)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.IndexOf((T)value);
}
}
public void Insert(int index, object value)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.Insert(index, (T)value);
}
}
public void Remove(object value)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.Remove((T)value);
}
}
public void RemoveAt(int index)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.RemoveAt(index);
}
}
T IList<T>.this[int index]
{
get
{
lock (syncRoot)
{
UpdateLists();
return underlyingList[index];
}
}
set
{
lock (syncRoot)
{
UpdateLists();
underlyingList[index] = value;
}
}
}
object IList.this[int index]
{
get { return ((IList<T>)this)[index]; }
set { ((IList<T>)this)[index] = (T)value; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool IsFixedSize
{
get { return false; }
}
public void Clear()
{
lock (syncRoot)
{
UpdateLists();
underlyingList.Clear();
}
}
public bool Contains(T item)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.CopyTo(array, arrayIndex);
}
}
public bool Remove(T item)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.Remove(item);
}
}
public void CopyTo(Array array, int index)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.CopyTo((T[])array, index);
}
}
public int Count
{
get
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.Count;
}
}
}
public object SyncRoot
{
get { return syncRoot; }
}
public bool IsSynchronized
{
get { return true; }
}
public int IndexOf(T item)
{
lock (syncRoot)
{
UpdateLists();
return underlyingList.IndexOf(item);
}
}
public void Insert(int index, T item)
{
lock (syncRoot)
{
UpdateLists();
underlyingList.Insert(index, item);
}
}
}
解决这个问题的经典方法是读写器锁。使用读写器锁,读写器不会阻止其他读写器使用锁,但是写写器会阻止读写器和其他写写器,以便它们可以安全地进行更改
如果您使用的是TPL,那么您当然可以访问。枚举列表时使用读卡器锁,从列表中添加或删除项目时使用写卡器锁。这将为您提供列表上下一级的可伸缩性。解决此问题的经典方法是读写器锁。使用读写器锁,读写器不会阻止其他读写器使用锁,但是写写器会阻止读写器和其他写写器,以便它们可以安全地进行更改
如果您使用的是TPL,那么您当然可以访问。枚举列表时使用读卡器锁,从列表中添加或删除项目时使用写卡器锁。这将为您提供列表上下一级的可扩展性。我猜您不会切换到ConcurrentDictionary,因为它需要更改代码-对吗?我猜您不会切换到ConcurrentDictionary,因为它需要更改代码-对吗?使用时要非常小心!要安全地枚举此列表是不可能的,因为计数可以从读取器下面更改。另外,我不确定您在那里使用ConcurrentQueue做什么;为什么需要RequireSync标志?ConcurrentQueue始终是线程安全的。使用时要非常小心!要安全地枚举此列表是不可能的,因为计数可以从读取器下面更改。另外,我不确定您在那里使用ConcurrentQueue做什么;为什么需要RequireSync标志?ConcurrentQueue始终是线程安全的。据我所知,这种类型的锁存在我提到的问题。写入请求是否排队并在读取请求之前到达?ReaderWriterLockSlim类是公平的,也就是说,一旦写入程序开始等待锁,就会阻止更多的读取程序,直到写入程序的请求得到满足。所以你不必担心饥饿。据我所知,这种锁存在我提到的问题。写入请求是否排队并在读取请求之前到达?ReaderWriterLockSlim类是公平的,也就是说,一旦写入程序开始等待锁,就会阻止更多的读取程序,直到写入程序的请求得到满足。所以你不必担心饥饿。