C# 使用PhysicalAddress作为键时,字典中存在重复键
因此,我遇到了一个有趣的问题,当使用PhysicalAddress类型的键时,我在C#Dictionary中得到了重复的键。这很有趣,因为它只发生在很长一段时间之后,我无法在完全不同的机器上使用相同的代码在单元测试中重现它。我可以在WindowsXPSP3机器上可靠地复制它,但必须让它一次运行几天,即使这样,它也只发生一次 下面是我正在使用的代码,下面是该部分代码的日志输出 代码: GetFromTagReports代码 获取重复密钥的日志输出:C# 使用PhysicalAddress作为键时,字典中存在重复键,c#,.net,dictionary,duplicates,C#,.net,Dictionary,Duplicates,因此,我遇到了一个有趣的问题,当使用PhysicalAddress类型的键时,我在C#Dictionary中得到了重复的键。这很有趣,因为它只发生在很长一段时间之后,我无法在完全不同的机器上使用相同的代码在单元测试中重现它。我可以在WindowsXPSP3机器上可靠地复制它,但必须让它一次运行几天,即使这样,它也只发生一次 下面是我正在使用的代码,下面是该部分代码的日志输出 代码: GetFromTagReports代码 获取重复密钥的日志输出: 2013-04-26 18:28:35,608
2013-04-26 18:28:35,608 [8] DEBUG ClassName - Detected tag (000CCC756081) with Exciter Id (0)
2013-04-26 18:28:35,608 [8] DEBUG ClassName - Detected tag is displayable (Unknown: ?56081)
2013-04-26 18:28:35,608 [8] TRACE ClassName - Keys
2013-04-26 18:28:35,608 [8] TRACE ClassName - Address: 000CCC755898
2013-04-26 18:28:35,608 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755A27
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755B47
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:35,618 [8] TRACE ClassName - Values
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755898 Name: Scotty McTester
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC755A27 Name: JDTest1
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC755B47 Name: 33 1
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:35,648 [8] TRACE ClassName - Current tags: Scotty McTester, ?56081, JDTest1, 33 1, ?56081
请注意,所有事情都发生在单个线程上(请参见[8]),因此字典不可能同时被修改。这些摘录来自同一日志和同一流程实例。还请注意,在第二组日志中,我们得到了两个相同的键
我正在研究的内容:我已将PhysicalAddress更改为字符串,以查看是否可以将其从嫌疑犯列表中删除
我的问题是:
- 在上面的代码中是否有我没有看到的问题
- 物理地址上的相等方法有问题吗?(只是偶尔会出错?)
- 这本词典有问题吗
var data = new byte[] { 1,2,3 };
var mac = new PhysicalAddress(data);
data[0] = 0;
其次,PhysicalAddress不是一个密封类,可以通过派生类进行更改
通过重写构造函数/GetHashCode/Equals方法实现。
但是这个用例看起来更像一个黑客,所以我们将忽略它,以及通过反射进行的修改
您的情况只能通过首先将PhysicalAddress对象放入dictionary来实现,
然后修改其源字节数组,然后将其包装到新的PhysicalAddress实例中
幸运的是,PhysicalAddress的GetHashCode实现只计算一次哈希,
如果修改了同一个实例,它仍然放在同一个字典桶中,
并再次以相等的方式定位
但是,如果源字节数组被传递到PhysicalAddress的另一个实例,其中
尚未计算-为新字节[]值重新计算哈希,找到新的存储桶,
并将副本插入字典。在极少数情况下,可以找到相同的铲斗
从新的散列,再次,没有重复插入
下面是重现问题的代码:
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
class App
{
static void Main()
{
var data = new byte[] { 1,2,3,4 };
var mac1 = new PhysicalAddress(data);
var mac2 = new PhysicalAddress(data);
var dictionary = new Dictionary<PhysicalAddress,string>();
dictionary[mac1] = "A";
Console.WriteLine("Has mac1:" + dictionary.ContainsKey(mac1));
//Console.WriteLine("Has mac2:" + dictionary.ContainsKey(mac2));
data[0] = 0;
Console.WriteLine("After modification");
Console.WriteLine("Has mac1:" + dictionary.ContainsKey(mac1));
Console.WriteLine("Has mac2:" + dictionary.ContainsKey(mac2));
dictionary[mac2] = "B";
foreach (var kvp in dictionary)
Console.WriteLine(kvp.Key + "=" + kvp.Value);
}
}
使用系统;
使用System.Collections.Generic;
使用System.Net.NetworkInformation;
类应用程序
{
静态void Main()
{
var data=新字节[]{1,2,3,4};
var mac1=新物理地址(数据);
var mac2=新物理地址(数据);
var dictionary=newdictionary();
字典[mac1]=“A”;
Console.WriteLine(“Has mac1:+dictionary.ContainsKey(mac1));
//Console.WriteLine(“Has mac2:+dictionary.ContainsKey(mac2));
数据[0]=0;
Console.WriteLine(“修改后”);
Console.WriteLine(“Has mac1:+dictionary.ContainsKey(mac1));
Console.WriteLine(“Has mac2:+dictionary.ContainsKey(mac2));
字典[mac2]=“B”;
foreach(字典中的var kvp)
控制台写入线(kvp.Key+“=”+kvp.Value);
}
}
注意注释行-如果我们取消注释它,“ContainsKey”方法将为mac2预计算哈希,即使在修改之后也是一样的
所以我的建议是找到一段生成PhysicalAddress实例的代码,并创建
为每个构造函数调用创建新的字节数组副本。您可以注意到非工作运行不会同时发生。这可能是线程问题的一个参数。如何确保
displayableTags
不是共享对象?这是局部变量吗?财产?此外,请使用TryGetValue
而不是ContainsKey
。我可以确定,因为“displayableTags”是在线程构造函数调用的方法中本地创建的变量。我尝试了TryGetValue,它也做了同样的事情(我将把它添加到问题中)。此外,来自TryGetValue上的msdn文档:此方法结合了ContainesKey方法的功能和Item属性。您可以在一个块中发布代码吗?问题可能是你的日志函数也有问题,我们可以看到吗?我本来不想添加太多代码,以防它弄乱文章。现在发布了更多代码。对PhysicalAddress上的Equals方法进行反编译表明,它只是比较底层字节数组——它检查长度,然后逐个元素遍历数组元素。您是使用接受字节[]的构造函数创建标记地址,还是使用PhysicalAddress.Parse(string)
?感谢您精心构造的答案:)不幸的是,我们已经在为每个构造函数调用创建新的字节数组。请参阅最近添加到问题中的代码。TagReport上的MacAddress属性在此之后再也不会分配给,只会被使用。另外,在那里创建的实例最终会进入GetTagReports调用?它在什么地方重复使用吗?字典的平均大小是多少?它多长时间被修改一次?没有更多的想法-尝试测试服务器的内存,在那个里你们可以重现这个问题。将所有字典访问方法放入lock(),以确保没有多线程问题。变量“physicalAddressBytes”在之后的任何位置都不会使用
var physicalAddressBytes = new byte[6];
ByteWriter.WriteBytesToBuffer(physicalAddressBytes, 0, protocolDataUnit.Payload, 4, 6);
var args = new TagReport
{
Version = protocolDataUnit.Version,
MacAddress = new PhysicalAddress(physicalAddressBytes),
BatteryStatus = protocolDataUnit.Payload[10],
ReceivedSignalStrength = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(protocolDataUnit.Payload, 12)),
ExciterId = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(protocolDataUnit.Payload, 14))
};
public static void WriteBytesToBuffer(byte[] oldValues, int oldValuesStartindex, byte[] newValues, int newValuesStartindex, int max)
{
var loopmax = (max > newValues.Length || max < 0) ? newValues.Length : max;
for (int i = 0; i < loopmax; ++i)
{
oldValues[oldValuesStartindex + i] = newValues[newValuesStartindex + i];
}
}
2013-04-26 18:28:34,347 [8] DEBUG ClassName - Detected tag (000CCC756081) with Exciter Id (0)
2013-04-26 18:28:34,347 [8] DEBUG ClassName - Detected tag is displayable (Unknown: ?56081)
2013-04-26 18:28:34,347 [8] TRACE ClassName - Keys
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755898
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755A27
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755B47
2013-04-26 18:28:34,347 [8] TRACE ClassName - Values
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755898 Name: Scotty McTester
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755A27 Name: JDTest1
2013-04-26 18:28:34,347 [8] TRACE ClassName - Address: 000CCC755B47 Name: 33 1
2013-04-26 18:28:34,347 [8] TRACE ClassName - Current tags: Scotty McTester, ?56081, JDTest1, 33 1
2013-04-26 18:28:35,608 [8] DEBUG ClassName - Detected tag (000CCC756081) with Exciter Id (0)
2013-04-26 18:28:35,608 [8] DEBUG ClassName - Detected tag is displayable (Unknown: ?56081)
2013-04-26 18:28:35,608 [8] TRACE ClassName - Keys
2013-04-26 18:28:35,608 [8] TRACE ClassName - Address: 000CCC755898
2013-04-26 18:28:35,608 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755A27
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755B47
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC756081
2013-04-26 18:28:35,618 [8] TRACE ClassName - Values
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC755898 Name: Scotty McTester
2013-04-26 18:28:35,618 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC755A27 Name: JDTest1
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC755B47 Name: 33 1
2013-04-26 18:28:35,648 [8] TRACE ClassName - Address: 000CCC756081 Name: ?56081
2013-04-26 18:28:35,648 [8] TRACE ClassName - Current tags: Scotty McTester, ?56081, JDTest1, 33 1, ?56081
var data = new byte[] { 1,2,3 };
var mac = new PhysicalAddress(data);
data[0] = 0;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
class App
{
static void Main()
{
var data = new byte[] { 1,2,3,4 };
var mac1 = new PhysicalAddress(data);
var mac2 = new PhysicalAddress(data);
var dictionary = new Dictionary<PhysicalAddress,string>();
dictionary[mac1] = "A";
Console.WriteLine("Has mac1:" + dictionary.ContainsKey(mac1));
//Console.WriteLine("Has mac2:" + dictionary.ContainsKey(mac2));
data[0] = 0;
Console.WriteLine("After modification");
Console.WriteLine("Has mac1:" + dictionary.ContainsKey(mac1));
Console.WriteLine("Has mac2:" + dictionary.ContainsKey(mac2));
dictionary[mac2] = "B";
foreach (var kvp in dictionary)
Console.WriteLine(kvp.Key + "=" + kvp.Value);
}
}