Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
541 views
in Technique[技术] by (71.8m points)

c# - 集合已修改; 枚举操作可能无法执行(Collection was modified; enumeration operation may not execute)

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

(我无法弄清此错误的原因,因为在连接调试器时,似乎没有发生此错误。)

Below is the code.

(下面是代码。)

This is a WCF server in a Windows service.

(这是Windows服务中的WCF服务器。)

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).

(每当有数据事件时,服务就会调用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.

(Windows Forms客户端进行预订时,订户ID被添加到订户字典中,而当客户端取消订阅时,将从该字典中删除它。)

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.

(看来,下次调用NotifySubscribers()方法时,foreach()循环因主题行中的错误而失败。)

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);
        }
    }
}
  ask by cdonner translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

(可能发生的情况是,SignalData在循环期间间接更改了后台的订户字典并导致该消息。)

You can verify this by changing

(您可以通过更改)

foreach(Subscriber s in subscribers.Values)

To

(至)

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

If I'm right, the problem will dissapear

(如果我说对了,问题就会消失)

Calling subscribers.Values.ToList() copies the values of subscribers.Values to a separate list at the start of the foreach .

(调用subscribers.Values.ToList()副本的值subscribers.Values在开始时单独列出foreach 。)

Nothing else has access to this list (it doesn't even have a variable name!), so nothing can modify it inside the loop.

(没有其他人可以访问此列表(它甚至没有变量名!),因此在循环内没有人可以对其进行修改。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...