Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 多线程同步列表<;T>;_C#_Multithreading_List_Generics - Fatal编程技术网

C# 多线程同步列表<;T>;

C# 多线程同步列表<;T>;,c#,multithreading,list,generics,C#,Multithreading,List,Generics,我有一个要求,即我需要存储一个项目列表的简单缓存。为此,我使用了List,但现在我们改变了设计,以适应多线程 系统的体系结构是由事件驱动的,因此读写操作很可能会发生冲突。由于绝大多数访问都是只读的,我认为ReaderWriterLockSlim可能是一个不错的选择。缓存只需要在该时刻的读取点准确 我已经写了下面的代码,它似乎工作正常,但是否有一些潜在的痛点 更新:虽然下面的代码确实解决了一些同步问题,但它并不是100%完美。此后,我决定实现一个简单得多的类,该类不会公开所有的IList操作,因此

我有一个要求,即我需要存储一个项目列表的简单缓存。为此,我使用了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();
        }

    }

}