收藏已修改;枚举操作可能无法执行 [英] Collection was modified; enumeration operation may not execute

查看:40
本文介绍了收藏已修改;枚举操作可能无法执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法深入了解这个错误,因为在附加调试器时,它似乎没有发生.

I can't get to the bottom of this error, because when the debugger is attached, it does not seem to occur.

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

Collection was modified; enumeration operation may not execute

下面是代码.

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

This is a WCF server in a Windows service. The method NotifySubscribers() is called by the service whenever there is a data event (at random intervals, but not very often - about 800 times per day).

当 Windows 窗体客户端订阅时,订阅者 ID 会添加到订阅者字典中,当客户端取消订阅时,它会从字典中删除.当(或之后)客户端取消订阅时,会发生错误.似乎下次调用 NotifySubscribers() 方法时,foreach() 循环失败,主题行中出现错误.该方法将错误写入应用程序日志,如下面的代码所示.当附加调试器并且客户端取消订阅时,代码执行良好.

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.

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

Do you see a problem with this code? Do I need to make the dictionary thread-safe?

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

推荐答案

可能发生的情况是 SignalData 在循环期间间接更改了底层的订阅者字典并导致该消息.您可以通过更改来验证这一点

What's likely happening is that SignalData is indirectly changing the subscribers dictionary under the hood during the loop and leading to that message. You can verify this by changing

foreach(Subscriber s in subscribers.Values)

foreach(Subscriber s in subscribers.Values.ToList())

如果我是对的,问题就会消失.

If I'm right, the problem will disappear.

调用 subscribers.Values.ToList() 会将 subscribers.Values 的值复制到 foreach 开头的单独列表中.没有其他东西可以访问这个列表(它甚至没有变量名!),所以没有任何东西可以在循环内修改它.

Calling subscribers.Values.ToList() copies the values of subscribers.Values to a separate list at the start of the foreach. Nothing else has access to this list (it doesn't even have a variable name!), so nothing can modify it inside the loop.

这篇关于收藏已修改;枚举操作可能无法执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆