首页 > C# > 集合已修改;枚举操作可能无法执行,如何处理。

集合已修改;枚举操作可能无法执行,如何处理。

上一篇 下一篇

我无法深入了解此错误,因为当附加调试器时,它似乎没有发生。集合已修改;枚举操作可能无法执行

下面是代码。

这是 Windows 服务中的 WCF 服务器。每当存在数据事件(以随机间隔,但不经常 – 每天大约 800 次)时,服务都会调用该方法。NotifySubscribers()

当 Windows 窗体客户端订阅时,订阅者 ID 将添加到订阅者字典中,当客户端取消订阅时,订阅者 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);
        }
    }
}

分割线

网友回答:

当订阅者取消订阅时,您将在枚举期间更改订阅服务器集合的内容。

有几种方法可以解决此问题,一种是将 for 循环更改为使用显式:.ToList()

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

分割线

网友回答:

可能发生的情况是,在循环期间间接更改了订阅者字典并导致该消息。您可以通过更改SignalData

foreach(Subscriber s in subscribers.Values)

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

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

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

分割线

网友回答:

在我看来,更有效的方法是有另一个列表,你声明你把任何“要删除”的东西放进去。然后在完成主循环后(没有 .ToList()),您可以在“要删除”列表上执行另一个循环,在发生时删除每个条目。所以在你的课堂上,你添加:

private List<Guid> toBeRemoved = new List<Guid>();

然后将其更改为:

public void NotifySubscribers(DataRecord sr)
{
    toBeRemoved.Clear();

    ...your unchanged code skipped...

   foreach ( Guid clientId in toBeRemoved )
   {
        try
        {
            subscribers.Remove(clientId);
        }
        catch(Exception e)
        {
            System.Diagnostics.Debug.WriteLine("Unsubscribe Error " + 
                e.Message);
        }
   }
}

...your unchanged code skipped...

public void UnsubscribeEvent(Guid clientId)
{
    toBeRemoved.Add( clientId );
}

这不仅可以解决您的问题,还可以防止您继续从字典中创建列表,如果那里有很多订阅者,这将很昂贵。假设在任何给定迭代中要删除的订阅者列表低于列表中的总数,这应该更快。但是,当然,如果您的特定使用情况有任何疑问,请随意对其进行分析,以确保情况确实如此。

模板简介:该模板名称为【集合已修改;枚举操作可能无法执行,如何处理。】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 248次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 02-20
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
单页式简历模板 微信素材 html5 自适应 微信图片 微信文章 微信公众平台 响应式 企业网站 微信模板
您可能会喜欢的其他模板