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

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

下面是代码。

这是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);
        }
    }
}

当前回答

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

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

其他回答

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

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

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

订阅者取消订阅时,您正在枚举期间更改订阅者集合的内容。

有几种方法可以解决这个问题,其中一种是改变for循环,使用显式的.ToList():

public void NotifySubscribers(DataRecord sr)  
{
    foreach(Subscriber s in subscribers.Values.ToList())
    {
                                              ^^^^^^^^^  
        ...

我也有同样的问题,当我使用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);
}

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

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

foreach(Subscriber s in subscribers.Values)

To

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

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

调用subscriber . values . tolist()复制订阅者的值。值赋给foreach开始时的单独列表。其他任何东西都不能访问这个列表(它甚至没有变量名!),所以在循环中没有东西可以修改它。