已使用锁并且仍然没有循环,异常 System.InvalidOperationException:集合已修改;枚举操作可能无法执行
lock used and there is no looping still, Exceptions System.InvalidOperationException: Collection was modified; enumeration operation may not execute
我已经经历过类似的问题,其中的答案建议使用 lock
而不是在同一集合的 foreach
循环中修改列表,我已经处理了这两个问题.
我有全局可用的 List<Object>
并且有两个不同的线程分别在列表中读取和写入。
为了避免竞争条件,我在两者(阅读[使用 linq] 和写作)中都使用了 Lock
。
而且我正在创建本地副本,修改它并将该副本恢复到主副本(以减少锁定时间)
I guess while creating a local copy, it is having reference of main
copy and thus main copy is being updated out side of lock too. Correct
me here!
包含类似代码供参考,
private void UpdateList(List<Object> newList)
{
List<Object> localCopyOfList;
lock(m_lockObj) //again a global object for locking
{
//I suspect it is reference type's problem here
localCopyOfList = m_globalCopyOfList;
}
foreach(Object obj in newList)
{
localCopyOfList.Add(obj);
}
lock(m_lockObj)
{
m_globalCopyOfList = localCopyOfList;
}
}
对于阅读,我有这样的方法,
private int GetTotalCount()
{
List<Object> localCopyOfList;
lock(m_lockObj) //again a global object for locking
{
localCopyOfList = m_globalCopyOfList;
}
//On following line I'm getting this exception
//Exceptions System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
return localCopyOfList.where(x => x.status == true).Count;
}
I suspect it is reference type's problem here.
是的。您不是在创建新列表,您只是将引用复制到同一个列表。
要根据现有值创建新列表,您可以使用 List copy constructor:
List<Object> localCopyOfList;
lock(m_lockObj) //again a global object for locking
{
//I suspect it is reference type's problem here
localCopyOfList = new List<Object>(m_globalCopyOfList);
}
foreach(Object obj in newList)
{
lockCopyOfList.Add(obj);
}
lock(m_lockObj)
{
m_globalCopyOfList = localCopyOfList;
}
请记住,localCopyOfList
不再引用与 m_globalCopyOfList
相同的集合,但它的元素引用了。
您只是将引用复制到锁中的同一列表。因此它们是同一个实例。您需要为本地创建一个新列表,并将全局列表中的内容复制到您的锁内。
我认为不需要任何锁定。
private void UpdateList(List<Object> newList)
{
//only read operation on global list, adding items only to local list
newList.AddRange(m_globalCopyOfList);
//reference assignment is atomic
m_globalCopyOfList = newList;
}
private int GetTotalCount()
{
//reference assignment is atomic
var localCopyOfList = m_globalCopyOfList;
//if UpdateList method is called anytime during below operation,
//m_globalCopyOfList ref will point to new list, localCopyOfList will not
//be touched.
return localCopyOfList.where(x => x.status == true).Count;
}
@Amit 我制作了包含 250 个任务的演示程序,每个任务调用 updatelist 和 getlist。我没有收到任何异常或数据丢失。不确定我是否遗漏了任何东西,但请检查下面的代码
class Program
{
static int Main(string[] args)
{
var obj = new Demo();
var t = Enumerable.Range(1, 500)
.Select((x, index) => Task.Run<int>(() => index %2 == 0
? obj.UpdateList(new List<object>() { new { status = true } })
: obj.GetTotalCount())).ToArray();
Task.WaitAll(t);
Console.Write(obj.GetTotalCount());
Console.ReadLine();
return 1;
}
public class Demo
{
private List<Object> m_globalCopyOfList { get; set; }
public Demo() {
m_globalCopyOfList = new List<object>();
}
public int UpdateList(List<Object> newList)
{
//only read operation on global list, adding items only to local list
newList.AddRange(m_globalCopyOfList);
//reference assignment is atomic
m_globalCopyOfList = newList;
return 1;
}
public int GetTotalCount()
{
//reference assignment is atomic
var localCopyOfList = m_globalCopyOfList;
//if UpdateList method is called anytime during below operation,
//m_globalCopyOfList ref will point to new list, localCopyOfList will not
//be touched.
Console.WriteLine("Current count " + localCopyOfList.Count);
return localCopyOfList.Count;
}
}
}
我已经经历过类似的问题,其中的答案建议使用 lock
而不是在同一集合的 foreach
循环中修改列表,我已经处理了这两个问题.
我有全局可用的 List<Object>
并且有两个不同的线程分别在列表中读取和写入。
为了避免竞争条件,我在两者(阅读[使用 linq] 和写作)中都使用了 Lock
。
而且我正在创建本地副本,修改它并将该副本恢复到主副本(以减少锁定时间)
I guess while creating a local copy, it is having reference of main copy and thus main copy is being updated out side of lock too. Correct me here!
包含类似代码供参考,
private void UpdateList(List<Object> newList)
{
List<Object> localCopyOfList;
lock(m_lockObj) //again a global object for locking
{
//I suspect it is reference type's problem here
localCopyOfList = m_globalCopyOfList;
}
foreach(Object obj in newList)
{
localCopyOfList.Add(obj);
}
lock(m_lockObj)
{
m_globalCopyOfList = localCopyOfList;
}
}
对于阅读,我有这样的方法,
private int GetTotalCount()
{
List<Object> localCopyOfList;
lock(m_lockObj) //again a global object for locking
{
localCopyOfList = m_globalCopyOfList;
}
//On following line I'm getting this exception
//Exceptions System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
return localCopyOfList.where(x => x.status == true).Count;
}
I suspect it is reference type's problem here.
是的。您不是在创建新列表,您只是将引用复制到同一个列表。
要根据现有值创建新列表,您可以使用 List copy constructor:
List<Object> localCopyOfList;
lock(m_lockObj) //again a global object for locking
{
//I suspect it is reference type's problem here
localCopyOfList = new List<Object>(m_globalCopyOfList);
}
foreach(Object obj in newList)
{
lockCopyOfList.Add(obj);
}
lock(m_lockObj)
{
m_globalCopyOfList = localCopyOfList;
}
请记住,localCopyOfList
不再引用与 m_globalCopyOfList
相同的集合,但它的元素引用了。
您只是将引用复制到锁中的同一列表。因此它们是同一个实例。您需要为本地创建一个新列表,并将全局列表中的内容复制到您的锁内。
我认为不需要任何锁定。
private void UpdateList(List<Object> newList)
{
//only read operation on global list, adding items only to local list
newList.AddRange(m_globalCopyOfList);
//reference assignment is atomic
m_globalCopyOfList = newList;
}
private int GetTotalCount()
{
//reference assignment is atomic
var localCopyOfList = m_globalCopyOfList;
//if UpdateList method is called anytime during below operation,
//m_globalCopyOfList ref will point to new list, localCopyOfList will not
//be touched.
return localCopyOfList.where(x => x.status == true).Count;
}
@Amit 我制作了包含 250 个任务的演示程序,每个任务调用 updatelist 和 getlist。我没有收到任何异常或数据丢失。不确定我是否遗漏了任何东西,但请检查下面的代码
class Program
{
static int Main(string[] args)
{
var obj = new Demo();
var t = Enumerable.Range(1, 500)
.Select((x, index) => Task.Run<int>(() => index %2 == 0
? obj.UpdateList(new List<object>() { new { status = true } })
: obj.GetTotalCount())).ToArray();
Task.WaitAll(t);
Console.Write(obj.GetTotalCount());
Console.ReadLine();
return 1;
}
public class Demo
{
private List<Object> m_globalCopyOfList { get; set; }
public Demo() {
m_globalCopyOfList = new List<object>();
}
public int UpdateList(List<Object> newList)
{
//only read operation on global list, adding items only to local list
newList.AddRange(m_globalCopyOfList);
//reference assignment is atomic
m_globalCopyOfList = newList;
return 1;
}
public int GetTotalCount()
{
//reference assignment is atomic
var localCopyOfList = m_globalCopyOfList;
//if UpdateList method is called anytime during below operation,
//m_globalCopyOfList ref will point to new list, localCopyOfList will not
//be touched.
Console.WriteLine("Current count " + localCopyOfList.Count);
return localCopyOfList.Count;
}
}
}