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;
公共静态类程序
{
普布利