Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/268.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.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# 如何安全地写入同一列表_C#_.net_Visual Studio 2017 - Fatal编程技术网

C# 如何安全地写入同一列表

C# 如何安全地写入同一列表,c#,.net,visual-studio-2017,C#,.net,Visual Studio 2017,我有一个公共静态列表DoggieList DoggieList在我的整个应用程序中由多个进程追加和写入 我们经常遇到这种异常: 收集被修改;枚举操作不能执行 假设有多个类写入DoggieList我们如何避开这个异常 请注意,这种设计不是很好,但在这一点上,我们需要在生产中快速修复它 我们如何从多个线程安全地对此列表执行变异? 我知道我们可以做如下事情: lock(lockObject) { DoggieList.AddRange(...) } 但是我们可以从多个类中针对同一个狗列表执行此

我有一个
公共静态列表DoggieList

DoggieList
在我的整个应用程序中由多个进程追加和写入

我们经常遇到这种异常:

收集被修改;枚举操作不能执行

假设有多个类写入
DoggieList
我们如何避开这个异常

请注意,这种设计不是很好,但在这一点上,我们需要在生产中快速修复它

我们如何从多个线程安全地对此列表执行变异?

我知道我们可以做如下事情:

lock(lockObject)
{
   DoggieList.AddRange(...)
}

但是我们可以从多个类中针对同一个
狗列表执行此操作吗?

使用
lock
a可以避免并发读取的缺点

不需要更改集合类型的有效解决方案是使用

使用以下扩展方法:

public static class ReaderWriterLockSlimExtensions
{
    public static void ExecuteWrite(this ReaderWriterLockSlim aLock, Action action)
    {
        aLock.EnterWriteLock();
        try
        {
            action();
        }
        finally
        {
            aLock.ExitWriteLock();
        }
    }

    public static void ExecuteRead(this ReaderWriterLockSlim aLock, Action action)
    {
        aLock.EnterReadLock();
        try
        {
            action();
        }
        finally
        {
            aLock.ExitReadLock();
        }
    }
}
可通过以下方式使用:

_lock.ExecuteWrite(() => DoggieList.Add(new Doggie()));

_lock.ExecuteRead(() =>
{
    // safe iteration
    foreach (MyDoggie item in DoggieList)
    {
        ....
    }
})
最后,如果您想在此基础上构建自己的收藏:

public class SafeList<T>
{
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private readonly List<T> _list = new List<T>();

    public T this[int index]
    {
        get
        {
            T result = default(T);

            _lock.ExecuteRead(() => result = _list[index]);

            return result;
        }
    }

    public List<T> GetAll()
    {
        List<T> result = null;

        _lock.ExecuteRead(() => result = _list.ToList());

        return result;
    }

    public void ForEach(Action<T> action) => 
      _lock.ExecuteRead(() => _list.ForEach(action));

    public void Add(T item) => _lock.ExecuteWrite(() => _list.Add(item));

    public void AddRange(IEnumerable<T> items) => 
      _lock.ExecuteWrite(() => _list.AddRange(items));
}
公共类安全列表
{
私有只读ReaderWriterLockSlim _lock=new ReaderWriterLockSlim();
私有只读列表_List=new List();
公共T此[int索引]
{
得到
{
T结果=默认值(T);
_lock.ExecuteRead(()=>result=_list[index]);
返回结果;
}
}
公共列表GetAll()
{
列表结果=空;
_lock.ExecuteRead(()=>result=\u list.ToList());
返回结果;
}
公共无效ForEach(行动)=>
_lock.ExecuteRead(()=>_list.ForEach(action));
public void Add(T项)=>_lock.ExecuteWrite(()=>_list.Add(项));
public void AddRange(IEnumerable items)=>
_lock.ExecuteWrite(()=>_list.AddRange(items));
}
此列表是完全安全的,多个线程可以并行添加或获取项,而不会出现任何并发问题。此外,多个线程可以并行获取项而不相互锁定,只有在编写集合时,一个以上的线程才能对集合工作


请注意,此集合未实现
IEnumerable
,因为您可能会获取枚举数并忘记处理它,这将使列表在读取模式下锁定。

使用
lock
a可以避免并发读取

不需要更改集合类型的有效解决方案是使用

使用以下扩展方法:

public static class ReaderWriterLockSlimExtensions
{
    public static void ExecuteWrite(this ReaderWriterLockSlim aLock, Action action)
    {
        aLock.EnterWriteLock();
        try
        {
            action();
        }
        finally
        {
            aLock.ExitWriteLock();
        }
    }

    public static void ExecuteRead(this ReaderWriterLockSlim aLock, Action action)
    {
        aLock.EnterReadLock();
        try
        {
            action();
        }
        finally
        {
            aLock.ExitReadLock();
        }
    }
}
可通过以下方式使用:

_lock.ExecuteWrite(() => DoggieList.Add(new Doggie()));

_lock.ExecuteRead(() =>
{
    // safe iteration
    foreach (MyDoggie item in DoggieList)
    {
        ....
    }
})
最后,如果您想在此基础上构建自己的收藏:

public class SafeList<T>
{
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private readonly List<T> _list = new List<T>();

    public T this[int index]
    {
        get
        {
            T result = default(T);

            _lock.ExecuteRead(() => result = _list[index]);

            return result;
        }
    }

    public List<T> GetAll()
    {
        List<T> result = null;

        _lock.ExecuteRead(() => result = _list.ToList());

        return result;
    }

    public void ForEach(Action<T> action) => 
      _lock.ExecuteRead(() => _list.ForEach(action));

    public void Add(T item) => _lock.ExecuteWrite(() => _list.Add(item));

    public void AddRange(IEnumerable<T> items) => 
      _lock.ExecuteWrite(() => _list.AddRange(items));
}
公共类安全列表
{
私有只读ReaderWriterLockSlim _lock=new ReaderWriterLockSlim();
私有只读列表_List=new List();
公共T此[int索引]
{
得到
{
T结果=默认值(T);
_lock.ExecuteRead(()=>result=_list[index]);
返回结果;
}
}
公共列表GetAll()
{
列表结果=空;
_lock.ExecuteRead(()=>result=\u list.ToList());
返回结果;
}
公共无效ForEach(行动)=>
_lock.ExecuteRead(()=>_list.ForEach(action));
public void Add(T项)=>_lock.ExecuteWrite(()=>_list.Add(项));
public void AddRange(IEnumerable items)=>
_lock.ExecuteWrite(()=>_list.AddRange(items));
}
此列表是完全安全的,多个线程可以并行添加或获取项,而不会出现任何并发问题。此外,多个线程可以并行获取项而不相互锁定,只有在编写集合时,一个以上的线程才能对集合工作


请注意,此集合未实现
IEnumerable
,因为您可能会获得一个枚举数并忘记处理它,这将使列表在读取模式下被锁定。

您也可以创建自己的类并封装锁定内容,您可以尝试如下操作:

您可以添加您想要的方法,如addRange、Remove等

class MyList { 

  private object objLock = new object(); 
  private List<int> list = new List<int>();


  public void Add(int value) {
    lock (objLock) {
      list.Add(value);
    }
  }

  public int Get(int index) {
   int val = -1;
   lock (objLock) {
      val = list[0];
    }
    return val;
  }

  public void GetAll() {
   List<int> retList = new List<int>();
   lock (objLock) {
      retList = new List<T>(list);
    }
    return retList;
  }
}
类MyList{
私有对象objLock=新对象();
私有列表=新列表();
公共void Add(int值){
锁(objLock){
列表。添加(值);
}
}
公共整数获取(整数索引){
int val=-1;
锁(objLock){
val=列表[0];
}
返回val;
}
public void GetAll(){
List retList=新列表();
锁(objLock){
retList=新列表(列表);
}
返回列表;
}
}

好东西:非常详细的并发集合:

使用并发集合类还可以解决与多线程更新相关的问题

范例

using System.Collections.Concurrent;
using System.Threading.Tasks;

public static class Program
{
    public static void Main()
    {
        var items = new[] { "item1", "item2", "item3" };
        var bag = new ConcurrentBag<string>();
        Parallel.ForEach(items, bag.Add);
    }
}
使用System.Collections.Concurrent;
使用System.Threading.Tasks;
公共静态类程序
{
公共静态void Main()
{
var items=new[]{“item1”、“item2”、“item3”};
var bag=新的ConcurrentBag();
并行。ForEach(项目、包、添加);
}
}

您也可以创建自己的类,并将锁定功能封装在其中,您可以尝试如下操作:

您可以添加您想要的方法,如addRange、Remove等

class MyList { 

  private object objLock = new object(); 
  private List<int> list = new List<int>();


  public void Add(int value) {
    lock (objLock) {
      list.Add(value);
    }
  }

  public int Get(int index) {
   int val = -1;
   lock (objLock) {
      val = list[0];
    }
    return val;
  }

  public void GetAll() {
   List<int> retList = new List<int>();
   lock (objLock) {
      retList = new List<T>(list);
    }
    return retList;
  }
}
类MyList{
私有对象objLock=新对象();
私有列表=新列表();
公共void Add(int值){
锁(objLock){
列表。添加(值);
}
}
公共整数获取(整数索引){
int val=-1;
锁(objLock){
val=列表[0];
}
返回val;
}
public void GetAll(){
List retList=新列表();
锁(objLock){
retList=新列表(列表);
}
返回列表;
}
}

好东西:非常详细的并发集合:

使用并发集合类还可以解决与多线程更新相关的问题

范例

using System.Collections.Concurrent;
using System.Threading.Tasks;

public static class Program
{
    public static void Main()
    {
        var items = new[] { "item1", "item2", "item3" };
        var bag = new ConcurrentBag<string>();
        Parallel.ForEach(items, bag.Add);
    }
}
使用System.Collections.Concurrent;
使用System.Threading.Tasks;
公共静态类程序
{
普布利