C# 多线程同步列表<;T>;
我有一个要求,即我需要存储一个项目列表的简单缓存。为此,我使用了ListC# 多线程同步列表<;T>;,c#,multithreading,list,generics,C#,Multithreading,List,Generics,我有一个要求,即我需要存储一个项目列表的简单缓存。为此,我使用了List,但现在我们改变了设计,以适应多线程 系统的体系结构是由事件驱动的,因此读写操作很可能会发生冲突。由于绝大多数访问都是只读的,我认为ReaderWriterLockSlim可能是一个不错的选择。缓存只需要在该时刻的读取点准确 我已经写了下面的代码,它似乎工作正常,但是否有一些潜在的痛点 更新:虽然下面的代码确实解决了一些同步问题,但它并不是100%完美。此后,我决定实现一个简单得多的类,该类不会公开所有的IList操作,因此
public class SynchronisedList<T> : IList<T>
{
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private IList<T> innerCache = new List<T>();
private U ReadReturn<U>(Func<U> function)
{
cacheLock.EnterReadLock();
try { return function(); }
finally { cacheLock.ExitReadLock(); }
}
private void Read(Action action)
{
cacheLock.EnterReadLock();
try { action(); }
finally { cacheLock.ExitReadLock(); }
}
private U WriteReturn<U>(Func<U> function)
{
cacheLock.EnterWriteLock();
try { return function(); }
finally { cacheLock.ExitWriteLock(); }
}
private void Write(Action action)
{
cacheLock.EnterWriteLock();
try { action(); }
finally { cacheLock.ExitWriteLock(); }
}
public T this[int index]
{
get { return ReadReturn(() => innerCache[index]); }
set { Write(() => innerCache[index] = value); }
}
public int IndexOf(T item) { return ReadReturn(() => innerCache.IndexOf(item)); }
public void Insert(int index, T item) { Write(() => innerCache.Insert(index, item)); }
public void RemoveAt(int index) { Write(() => innerCache.RemoveAt(index)); }
public void Add(T item) { Write(() => innerCache.Add(item)); }
public void Clear() { Write(() => innerCache.Clear()); }
public bool Contains(T item) { return ReadReturn(() => innerCache.Contains(item)); }
public int Count { get { return ReadReturn(() => innerCache.Count); } }
public bool IsReadOnly { get { return ReadReturn(() => innerCache.IsReadOnly); } }
public void CopyTo(T[] array, int arrayIndex) { Read(() => innerCache.CopyTo(array, arrayIndex)); }
public bool Remove(T item) { return WriteReturn(() => innerCache.Remove(item)); }
public IEnumerator<T> GetEnumerator() { return ReadReturn(() => innerCache.GetEnumerator()); }
IEnumerator IEnumerable.GetEnumerator() { return ReadReturn(() => (innerCache as IEnumerable).GetEnumerator()); }
}
internal class Program
{
private static SynchronisedList<int> list = new SynchronisedList<int>();
private static void Main()
{
for (int i = 0; i < 500000; i++)
{
var index = i;
ThreadPool.QueueUserWorkItem((state) =>
{
var threadNum = (int)state;
if (threadNum % 10 == 0)
{
list.Add(threadNum);
}
else
{
Console.WriteLine(list.Count);
}
}, index);
}
Console.ReadKey();
}
}
公共类同步列表:IList
{
private ReaderWriterLockSlim cacheLock=新的ReaderWriterLockSlim();
私有IList innerCache=新列表();
专用U读返回(Func函数)
{
cacheLock.EnterReadLock();
尝试{return function();}
最后{cacheLock.exitradlock();}
}
私有无效读取(操作)
{
cacheLock.EnterReadLock();
试试{action();}
最后{cacheLock.exitradlock();}
}
专用U写返回(Func函数)
{
cacheLock.EnterWriteLock();
尝试{return function();}
最后{cacheLock.ExitWriteLock();}
}
私有无效写入(操作)
{
cacheLock.EnterWriteLock();
试试{action();}
最后{cacheLock.ExitWriteLock();}
}
公共T此[int索引]
{
获取{return ReadReturn(()=>innerCache[index]);}
设置{Write(()=>innerCache[index]=value);}
}
public int IndexOf(T项){return ReadReturn(()=>innerCache.IndexOf(项));}
public void Insert(int-index,T-item){Write(()=>innerCache.Insert(index,item));}
public void RemoveAt(int-index){Write(()=>innerCache.RemoveAt(index));}
public void Add(T项){Write(()=>innerCache.Add(项));}
public void Clear(){Write(()=>innerCache.Clear());}
公共bool包含(T项){return ReadReturn(()=>innerCache.Contains(项));}
公共int计数{get{return ReadReturn(()=>innerCache.Count);}
public bool IsReadOnly{get{return ReadReturn(()=>innerCache.IsReadOnly);}
public void CopyTo(T[]array,int-arrayIndex){Read(()=>innerCache.CopyTo(array,arrayIndex));}
public bool Remove(T项){return WriteReturn(()=>innerCache.Remove(项));}
公共IEnumerator GetEnumerator(){return ReadReturn(()=>innerCache.GetEnumerator());}
IEnumerator IEnumerable.GetEnumerator(){return ReadReturn(()=>(内部缓存为IEnumerable.GetEnumerator());}
}
内部课程计划
{
私有静态同步列表=新同步列表();
私有静态void Main()
{
对于(int i=0;i<500000;i++)
{
var指数=i;
ThreadPool.QueueUserWorkItem((状态)=>
{
var threadNum=(int)状态;
如果(threadNum%10==0)
{
list.Add(threadNum);
}
其他的
{
Console.WriteLine(list.Count);
}
},指数);
}
Console.ReadKey();
}
}
这里有几个线程问题
一,。
我认为GetEnumerator函数在这里暴露了一个线程问题。它们提供了对innerCache的引用,而innerCache不受锁的控制
例如,如果一个线程在列表上执行foreach操作,而另一个线程正在删除或插入元素,则可能会出现故障
解决方案是复制列表,并在新克隆的列表上返回一个枚举数。如果列表很长,则会出现内存问题
二,。
除非在同步列表之外有另一个锁定方法,否则Contains()和IndexOf()函数或多或少都是无用的
示例:线程A获取对象的索引,线程B插入/删除/更新该对象,线程A索引现在已过时
我不认为这是一个伟大的想法,真的有一个完全同步的列表。编写一个功能有限的定制版本
如果您只需要一个队列或堆栈,请仅使用两个或三个完全同步的必要方法来实现该队列或堆栈。如果您需要更多的功能,请使用列表并让不同的线程进行同步。您知道内置类吗
它使用基于标准
监视器的锁定,而不是ReaderWriterLockSlim
。您需要对其进行分析,以确定这是否会在您的特定使用场景中产生显著的性能差异。您的实现是正常的,但您仍然需要关注同步问题:
给定一个列表{“foo”}
现在,如果另一个线程在这两行之间执行list.Clear()呢?
您的读写器锁应该可以公开访问以处理这些情况。
当然,枚举数也是如此
试着列一个清单是所有的事情
所有人和线程安全是
很难
我认为您应该查看应用程序需要的操作,然后设计一个类,以线程安全的方式公开这些操作。(列表级别太低)
在现实生活中,您的设计不可能是线程安全的,因为调用列表的代码可能以不安全的方式组合操作
只需公开一个枚举器就可以打开一个
很多问题——这对我来说意味着什么
访问列表中的所有项目时
另一个线程正在更改列表
这个类解决了
int index = list.IndexOf("foo");
Console.WriteLine(list[index]);
List<T> unsafeList = ...
var threadSafeList = new SyncronisedList(unsafeList);
using (threadSafeList.EnterReadScope()) {
// all your sequential read operations are thread-safe
}
using (threadSafeList.EnterWriteScope()) {
// all sequential read/write operations are thread-safe
}
public class SyncronisedList<T> : IList<T> {
private readonly ReaderWriterLockSlim _threadLock;
private readonly IList<T> _internalList;
public SyncronisedList() : this(new List<T>()) {
}
public SyncronisedList(IList<T> internalList) {
_internalList = internalList;
_threadLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
}
private U Read<U>(Func<U> function) {
using (EnterReadScope())
return function();
}
private void Read(Action action) {
using (EnterReadScope())
action();
}
private U Write<U>(Func<U> function) {
using (EnterWriteScope())
return function();
}
private void Write(Action action) {
using (EnterWriteScope())
action();
}
public IDisposable EnterReadScope() {
return new Scope<T>(this, false);
}
public IDisposable EnterWriteScope() {
return new Scope<T>(this, true);
}
public T this[int index] {
get { return Read(() => _internalList[index]); }
set { Write(() => _internalList[index] = value); }
}
public int IndexOf(T item) { return Read(() => _internalList.IndexOf(item)); }
public void Insert(int index, T item) { Write(() => _internalList.Insert(index, item)); }
public void RemoveAt(int index) { Write(() => _internalList.RemoveAt(index)); }
public void Add(T item) { Write(() => _internalList.Add(item)); }
public void Clear() { Write(() => _internalList.Clear()); }
public bool Contains(T item) { return Read(() => _internalList.Contains(item)); }
public int Count { get { return Read(() => _internalList.Count); } }
public bool IsReadOnly { get { return Read(() => _internalList.IsReadOnly); } }
public void CopyTo(T[] array, int arrayIndex) { Read(() => _internalList.CopyTo(array, arrayIndex)); }
public bool Remove(T item) { return Write(() => _internalList.Remove(item)); }
public IEnumerator<T> GetEnumerator() { return Read(() => _internalList.GetEnumerator()); }
IEnumerator IEnumerable.GetEnumerator() { return Read(() => (_internalList as IEnumerable).GetEnumerator()); }
private class Scope<U> : IDisposable {
private readonly SyncronisedList<U> _owner;
private readonly bool _write;
internal Scope(SyncronisedList<U> owner, bool write) {
_owner = owner;
_write = write;
if (_write)
_owner._threadLock.EnterWriteLock();
else
_owner._threadLock.EnterReadLock();
}
public void Dispose() {
if (_write)
_owner._threadLock.ExitWriteLock();
else
_owner._threadLock.ExitReadLock();
}
}
}