即使我锁定它,我的列表也不是线程安全的

时间:2012-06-07 09:24:31

标签: c# multithreading list locking sync

我得到了:

“集合被修改的枚举操作可能无法执行”在此行:

foreach (Message msg in queue)
过了一会儿。

我必须使用.NET 2.0。

我在私人列表<>上进行的两项操作名为“队列”的如下:

// 1st function calls this
lock (queue)
{
      queue.Add(msg);
}

// 2nd function calls this
lock (queue)
{
      using (StreamWriter outfile = new StreamWriter("path", true)
      {
             foreach (Message msg in queue) // This is were I get the exception after a while)
             {
                  outfile.WriteLine(JsonConvert.SerializeObject(msg)); 
             }

              queue = new List<Message>();
      }
}

我做错了什么?

2 个答案:

答案 0 :(得分:3)

(下面;好吧,我无法想出会导致这种情况的竞争条件......但是:谁知道......)

首先,真的需要查找与列表对话的其他代码;问题不在你发布的代码中。我怎么知道这个?因为在您枚举(foreach (Message msg in queue))时,您对queue有一个锁定,而我们对锁定对象的(非常狡猾但无关)重新分配没有做任何事情。

对于此foreach错误,意味着其他内容正在改变列表。首先要做的是,重命名列表字段。如果其他代码触及列表,这将很快显示。另外,请检查您是否公开此代码之外的列表,即从任何地方永远不会return queue;

问题似乎不在您显示的代码中。重新分配锁定对象是不好的做法,你不应该这样做 - 但是:我看不到一个场景(显示代码)它实际上打破它。


列表不是这里最好的模型,重新分配锁定对象是一个好主意。 如果只有一个内置类型用于表示队列 ...

private readonly Queue<Message> queue = new Queue<Message>();
...
lock (queue) {
    queue.Enqueue(msg);
}

// 2nd function calls this
lock (queue) {
    if(queue.Count == 0) continue; // taken from comments

    using (StreamWriter outfile = new StreamWriter("path", true) {
        while(queue.Count != 0) {
            Message msg = queue.Dequeue();
            outfile.WriteLine(JsonConvert.SerializeObject(msg)); 
        }
    }
}

无需清除,因为Dequeue具有内在和有效的功能。

答案 1 :(得分:1)

lick语句使用的参数应该是readonly。见link

使用readonly private object代替queqe

代码应

eadonly object _object = new object();
// 1st function calls this
lock (_object)
{
      queue.Add(msg);
}

// 2nd function calls this
lock (_object)
{
      using (StreamWriter outfile = new StreamWriter("path", true)
      {
             foreach (Message msg in queue) // This is were I get the exception after a while)
             {
                  outfile.WriteLine(JsonConvert.SerializeObject(msg)); 
             }

              queue = new List<Message>();
      }
}