解决集合周围的并发问题

the*_*onk 2 .net c# collections concurrency thread-safety

我正在处理与我正在进行的多玩家同步游戏同时使用共享集合的问题.我做了一些挖掘,在Alexey Drobyshevsky关于codeproject的帖子中发现了一个整洁的线程安全的IEnumerator/IList实现:

http://www.codeproject.com/KB/cs/safe_enumerable.aspx

在采用他的实现后,我甚至用for/foreach循环替换了共享集合上的所有Linq查询,因为Linq查询仍在使用下面的不安全的IEnumerable.

这是我对SafeList的实现,列表本身作为消费类的ReadOnlyCollection公开.

http://theburningmonk.com/2010/03/thread-safe-enumeration-in-csharp/

切换到这个SafeList之后,我看到的问题要少得多,但是在负载很重的情况下(80多个线程都在不同的点读取/写入列表)我仍然看到抛出InvalidOperationException:

元素列表已更改.枚举操作无法继续

我甚至尝试使用ReadWriterLockSlim来代替我在SafeList实现中的锁对象,但事实证明这也没有结果.到目前为止,我唯一的另一个建议是每次线程需要循环时克隆列表.我希望避免每次都克隆列表,因为列表的使用方式太多,可能会影响性能并可能引入其他难以发现的错误.

由于时间限制,我不得不务实一下,如果克隆是去解决这个问题,然后,我与它的罚款最安全快捷的方式,但采取这种最后的挣扎,尝试之前,我只是想知道如果有人在那里遇到类似的东西能够提供一些建议.

提前谢谢了!

[编辑]以下是我所看到的问题的更多信息:

对于一个"游戏",可以有多达100个左右的同步客户端连接,并且游戏需要每隔几秒向每个连接的客户端发送更新消息,因此每隔几秒这个游戏需要遍历共享的玩家列表.当玩家加入或离开时,需要相应地更新列表以反映更改.除此之外,玩家可以与游戏交互并与其他玩家聊天,并且每次从玩家接收消息时,游戏将再次需要迭代通过相同的列表并进行广播.当游戏试图在许多玩家同时离开/加入(写入操作)的同时向玩家广播消息(读取操作)时,通常会抛出异常.

Dan*_*ker 6

鉴于您对游戏结构的描述,考虑使用单个线程,这是唯一可以直接访问玩家列表的线程.使列表有效地专用于该一个线程.

任何其他线程访问列表的方式是通过向列表管理器线程发送消息.因此该线程有一个等待的消息队列.虽然队列是非空的,但它会按照指示咀嚼消息.他们可能会说"添加新玩家",或"删除玩家",或"将此玩家的状态更新为'不快乐'".

此线程还可以定期扫描列表以将更新发送到客户端,或者它可以(甚至更好地)使用它具有列表中发生的已知更改流的事实,并将这些更改转发给客户端.

基本原则:将数据专用于一个线程,并让线程通过消息队列进行通信.

您的基本数据结构是一个线程安全的队列类.SO已有数十个例子.(并避开任何声称"无锁"但又安全的线程.这不值得冒风险.)