陷入一个奇怪的“问题”。有一个应用程序pinging整个网络。在你使用255.255.0.0网络掩码(这是65k +地址)的网络之前一直运行良好。
我发送这样的ping:
foreach (string str in ListContainingAddresses)
{
using (Ping ping = new Ping())
{
if (pingCounter == 10000) { Thread.Sleep(10000); pingCounter = 0; }
//Make an eventhandler
ping.PingCompleted += new PingCompletedEventHandler(pingCompleted);
//Send the pings asynchronously
ping.SendAsync(IPAddress.Parse(str), 1000);
sentPings++;
//This counts pings being sent out
pingCounter++;
}
}
并像这样回忆他们:
public void pingCompleted(object sender, PingCompletedEventArgs e)
{
//This counts recieved addresses
recievedIpAddresses++;
if (e.Reply.Status == IPStatus.Success)
{
//Do something
}
else
{
/*Computer is down*/
}
//This checks if sent equals recieved
if (recievedIpAddresses == sentPings )
{
//All returned
}
}
问题是a)有时(非常罕见)它没有完成(条件不符合)。 b)什么时候完成数字不匹配?如果我打印发送和收到刚刚他们是
Sent: 65025 Recieved: 64990
尽管如此,条件得到满足并且申请仍在继续?我不知道为什么以及如何发生这种情况。代码是否快速执行,以便应用程序更新两个整数?沿途会有一些丢失吗?如果我在具有255个地址的子网上尝试它,这个问题永远不会发生。 自.NET 3.5
以来,不能使用CountDownEvent而不是变量答案 0 :(得分:7)
你有任何锁定吗?这看起来像你的问题。我可以在您的代码中看到各种race conditions and memory processor cache issues。
尝试使用lock
保护recievedIpAddresses == sentPings
和
sentPings++;
//This counts pings being sent out
pingCounter++;
lock
例如:
private readonly object SyncRoot = new object();
public void MainMethod()
{
foreach (string str in ListContainingAddresses)
{ ... }
lock (SyncRoot) { sentPings++; }
....
}
public void pingCompleted(object sender, PingCompletedEventArgs e)
{
//This counts recieved addresses
lock (SyncRoot) { recievedIpAddresses++; } // lock this if it is used on other threads
if (e.Reply.Status == IPStatus.Success)
{
//Do something
}
else
{
/*Computer is down*/
}
lock (SyncRoot) { // lock this to ensure reading the right value of sentPings
//This checks if sent equals recieved
if (recievedIpAddresses == sentPings )
{
//All returned
}
}
}
上面的示例将强制从共享内存中读取和写入,以便不同的CPU内核不会读取不同的值。但是,根据您的代码,您可能需要更多粗粒度锁定,其中第一个循环保护sentPings
中的pingCounter
和lock
,甚至第二个方法完全受到保护lock
。
人们可以说不使用lock
因为它会导致性能问题,而且免费锁定非常时髦。在大多数情况下,底线lock
比其他替代方案更简单。您可能需要使锁定比上面的样本更粗糙,因为您可能也有竞争条件。如果没有看到整个程序,很难提供更好的样本。
Interlocked.Increment
这里使用lock
的主要原因是强制每次读取和写入来自内存,而不是CPU缓存,因此您应该获得一致的值。锁定的替代方法是使用Interlocked.Increment,但如果您在两个单独的变量上使用它,则需要仔细观察竞争条件。
(编辑)
即使您锁定了您可能有问题。观看13个目标地址的时间表(不幸的是)。如果您对此不满意,请查看"Managed Threading Basics"和"Threading in C# - Joseph Albahari"
sentPings++
sentPings++
(现在等于13)recievedIpAddresses == sentPings
测试 - 现在因为不相等而失败pingCompleted
并执行recievedIpAddresses++;
您需要在代码中仔细观察此类型的竞争条件,并相应地进行调整。线程的全部内容是它们与操作重叠。
脚注:
为什么SyncRoot
声明为:private readonly object SyncRoot = new object();
?
static
控制台应用,则需要static
。但是,如果在类中使用static
,则每个实例都将锁定同一个对象,因此会出现争用readonly
,并阻止您(或其他团队成员)稍后覆盖object
:
SyncRoot
为例; Visual Studio历来称之为“它的片段”