收藏已修改; 枚举操作可能不会执行
我无法得到这个错误的底部,因为当调试器被连接时,它似乎不会发生。 以下是代码。
这是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);
}
}
}
可能发生的情况是,SignalData在循环过程中间接更改用户字典并导致该消息。 您可以通过更改来验证这一点
foreach(Subscriber s in subscribers.Values)
至
foreach(Subscriber s in subscribers.Values.ToList())
如果我是对的,问题就会消失
当订阅者取消订阅时,您将在枚举期间更改订阅者集合的内容。
有几种方法可以解决这个问题,一种方法是将for循环改为使用显式的.ToList()
:
public void NotifySubscribers(DataRecord sr)
{
foreach(Subscriber s in subscribers.Values.ToList())
{
^^^^^^^^^
...
在我看来,更有效的方法是制定另一个列表,声明您将任何“要删除”的内容放入。 然后,在完成主循环(没有.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 );
}
这不仅能解决你的问题,还能防止你不得不从你的字典中创建一个列表,如果那里有很多用户,这个列表很贵。 假设在任何给定迭代中要删除的用户列表都低于列表中的总数,这应该会更快。 但当然,如果您对特定使用情况有任何疑问,可以自由地对其进行描述以确保是这种情况。
链接地址: http://www.djcxy.com/p/51423.html上一篇: Collection was modified; enumeration operation may not execute