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

落爺英雄遲暮 提交于 2019-12-17 13:08:44

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

我无法弄清此错误的原因,因为在连接调试器时,似乎没有发生此错误。 下面是代码。

这是Windows服务中的WCF服务器。 每当有数据事件时,服务就会调用NotifySubscribers方法(以随机间隔,但不是很频繁-每天大约800次)。

Windows Forms客户端进行预订时,订户ID被添加到订户字典中,而当客户端取消订阅时,将从该字典中删除它。 该错误发生在客户退订时(或之后)。 看来,下次调用NotifySubscribers()方法时,foreach()循环因主题行中的错误而失败。 该方法将错误写入应用程序日志,如下面的代码所示。 连接调试器后,如果客户端取消订阅,则代码可以正常执行。

您看到此代码有问题吗? 我需要使字典具有线程安全性吗?

[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);
        }
    }
}

#1楼

实际上,在我看来,问题似乎在于您正在从列表中删除元素,并希望继续读取列表,就好像什么都没发生一样。

您真正需要做的是从头开始,然后再回到头。 即使您从列表中删除了元素,您也可以继续阅读它。


#2楼

您还可以锁定您的订户字典,以防止其在循环时被修改:

 lock (subscribers)
 {
         foreach (var subscriber in subscribers)
         {
               //do something
         }
 }

#3楼

您可以将订户字典对象复制到相同类型的临时字典对象,然后使用foreach循环迭代该临时字典对象。


#4楼

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


#5楼

我有同样的问题,当我使用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);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!