我无法找到这个错误的根源,因为当附加调试器时,它似乎没有发生。

修改集合;枚举操作可能无法执行

下面是代码。

这是Windows服务中的WCF服务器。只要有数据事件,服务就会调用NotifySubscribers()方法(随机间隔,但不经常——大约每天800次)。

When a Windows Forms client subscribes, the subscriber ID is added to the subscribers dictionary, and when the client unsubscribes, it is deleted from the dictionary. The error happens when (or after) a client unsubscribes. It appears that the next time the NotifySubscribers() method is called, the foreach() loop fails with the error in the subject line. The method writes the error into the application log as shown in the code below. When a debugger is attached and a client unsubscribes, the code executes fine.

您认为这段代码有问题吗?我需要使字典线程安全吗?

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class SubscriptionServer : ISubscriptionServer
{
    private static IDictionary<Guid, Subscriber> subscribers;

    public SubscriptionServer()
    {            
        subscribers = new Dictionary<Guid, Subscriber>();
    }

    public void NotifySubscribers(DataRecord sr)
    {
        foreach(Subscriber s in subscribers.Values)
        {
            try
            {
                s.Callback.SignalData(sr);
            }
            catch (Exception e)
            {
                DCS.WriteToApplicationLog(e.Message, 
                  System.Diagnostics.EventLogEntryType.Error);

                UnsubscribeEvent(s.ClientId);
            }
        }
    }
    
    public Guid SubscribeEvent(string clientDescription)
    {
        Subscriber subscriber = new Subscriber();
        subscriber.Callback = OperationContext.Current.
                GetCallbackChannel<IDCSCallback>();

        subscribers.Add(subscriber.ClientId, subscriber);
        
        return subscriber.ClientId;
    }

    public void UnsubscribeEvent(Guid clientId)
    {
        try
        {
            subscribers.Remove(clientId);
        }
        catch(Exception e)
        {
            System.Diagnostics.Debug.WriteLine("Unsubscribe Error " + 
                    e.Message);
        }
    }
}

当前回答

我也有同样的问题,当我使用for循环而不是foreach时,它得到了解决。

// foreach (var item in itemsToBeLast)
for (int i = 0; i < itemsToBeLast.Count; i++)
{
    var matchingItem = itemsToBeLast.FirstOrDefault(item => item.Detach);

   if (matchingItem != null)
   {
      itemsToBeLast.Remove(matchingItem);
      continue;
   }
   allItems.Add(itemsToBeLast[i]);// (attachDetachItem);
}

其他回答

因此,解决这个问题的另一种方法是创建一个新的字典,而不是删除元素,只添加你不想删除的元素,然后用新的字典替换原来的字典。我不认为这是一个太大的效率问题,因为它不会增加你迭代结构的次数。

我也有同样的问题,当我使用for循环而不是foreach时,它得到了解决。

// foreach (var item in itemsToBeLast)
for (int i = 0; i < itemsToBeLast.Count; i++)
{
    var matchingItem = itemsToBeLast.FirstOrDefault(item => item.Detach);

   if (matchingItem != null)
   {
      itemsToBeLast.Remove(matchingItem);
      continue;
   }
   allItems.Add(itemsToBeLast[i]);// (attachDetachItem);
}

公认的答案在最坏的情况下是不精确和不正确的。如果在ToList()期间进行了更改,您仍然会得到一个错误。除了锁(如果您有一个公共成员,则需要考虑锁的性能和线程安全性)之外,一个合适的解决方案是使用不可变类型。

一般来说,不可变类型意味着一旦创建了它,就不能更改它的状态。 所以你的代码应该是这样的:

public class SubscriptionServer : ISubscriptionServer
{
    private static ImmutableDictionary<Guid, Subscriber> subscribers = ImmutableDictionary<Guid, Subscriber>.Empty;
    public void SubscribeEvent(string id)
    {
        subscribers = subscribers.Add(Guid.NewGuid(), new Subscriber());
    }
    public void NotifyEvent()
    {
        foreach(var sub in subscribers.Values)
        {
            //.....This is always safe
        }
    }
    //.........
}

如果您有一个公共成员,这可能特别有用。其他类总是可以对不可变类型进行foreach,而不用担心集合被修改。

好的,帮助我的是向后迭代。我试图从列表中删除一个条目,但向上迭代,它搞砸了循环,因为该条目不再存在了:

for (int x = myList.Count - 1; x > -1; x--)
{
    myList.RemoveAt(x);
}

这种方法应该可以覆盖在函数仍在执行时再次调用(并且项只需要使用一次)的并发情况:

 while (list.Count > 0)
 {
    string Item = list[0];
    list.RemoveAt(0);
 
    // do here what you need to do with item
 
 } 
 

如果函数在仍在执行时被调用,则项目将不会从第一次再次重申,因为它们在使用时立即被删除。 对于小列表应该不会对性能产生太大影响。