【发布时间】:2020-04-19 10:09:11
【问题描述】:
我看到一些奇怪的行为,但无法弄清楚它是如何发生的。我有 2 个排序集,我使用缓冲区按序列号重新排序 TCP 数据包,并且我正在锁定语句中从这些集中读取数据包。
这是一个简化的例子:
SortedSet<Packet> in = new SortedSet<TcpPacket>(new SeqComparer()); // inbound buffer
SortedSet<Packet> out = new SortedSet<TcpPacket>(new SeqComparer()); // outbound buffer
public void BufferPacket(Packet packet) {
// Some code which does not modify buffers
bool buffer = isOutbound ? out : in;
lock (buffer) {
buffer.Add(packet);
Packet bufferedPacket;
// Removing retransmitted packets
while (buffer.Count > 0 && (bufferedPacket = buffer.Min()).SeqNum < expected) {
buffer.Remove(bufferedPacket);
}
// Process buffer
while (buffer.Count > 0 && (bufferedPacket = buffer.Min()).SeqNum == expected) {
buffer.Remove(bufferedPacket);
expected += bufferedPacket.Length;
...
}
}
}
有些东西导致了这个异常:
System.ArgumentException: At least one object must implement IComparable.
at System.Collections.Comparer.Compare(Object a, Object b)
at System.Linq.Enumerable.Min[TSource](IEnumerable`1 source)
这似乎暗示在检查Count > 0 之后,Min() 没有发现任何对象。我对锁定时如何发生这种情况感到非常困惑。
【问题讨论】:
-
lock不会锁定集合/集合,它会阻止其他线程进入以下语句。如果你有其他地方可以修改buffer,它不使用相同的lock,仍然可以修改集合/集合。 -
可以分享
Packet的签名吗?它必须实现IComparable才能使用Min。 -
对不起,我的示例实际上遗漏了我已将比较器传递给 SortedSet 以比较数据包的 seqnum。代码大部分时间运行良好,但随机遇到此故障。
-
Min是一种 LINQ 方法,它不使用您传递给SortedSet的Comparer。当集合中只有 0 到 1 个对象时,Min不会比较对象。如果集合包含 2 个或更多项目,则Min比较Packet并且您将得到类似的异常。您能否验证当此代码有效时,集合中有多少项? -
因为集合是有序的,所以你不需要
Min,你可以使用First()或Last(),这取决于你的比较器如何比较。
标签: c# linq networking concurrency locking