Ran into a weird "problem". Have an application that pingsweeps entire networks. Works great until you get to a network with 255.255.0.0 netmask (which is 65k + addresses).
I send my pings like this:
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++;
}
}
And recive them like this:
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
}
}
Problem is that a) Sometimes (very rarely) it doesnt complete (Condition not met). b) When it does complete the numbers dont match? If i print sent and recieved just now they are
Sent: 65025 Recieved: 64990
Despite this the condition is met and application moves on? I dont know why and how this is happening. Is the code executing to fast for the application to update two ints? Do some pings get lost along the way? If I try it on a subnetwork with 255 addresses this problem never happens. Cant use CountDownEvent instead of variables since its .NET 3.5
Continuous ping by a Windows computer to the IP address 93.184.216.34 For each incoming response packet, ping issues an entry on the standard output (stdout).
Follow the instructions below to run ping in MacOS as a continuous test: Step 1: Open the terminal. You’ll find the Mac terminal under “ Applications” in the subfolder “Utilities ”. Step 2: Run the ping command with the address of the target computer.
First on the list: Ping.SendAsync (): Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message to a computer, and receive a corresponding ICMP echo reply message from that computer. - MSDN
If you’d like to have the continuous ping issue a timestamp, use ping with the -D option. In this case, the output for each incoming response packet is preceded by a UNIX timestamp. If you don’t want to run ping on an endless loop in Linux, define the ping quantity with the -c option according to the following example.
Do you have any locking in place at all? That looks like your problem to me. I can see all sorts of race conditions and memory processor cache issues potentially in your code.
Try using lock
for protecting the recievedIpAddresses == sentPings
and
sentPings++;
//This counts pings being sent out
pingCounter++;
lock
For instance:
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
}
}
}
The above sample will force reads and writes from shared memory so that different CPU cores aren't reading different values. However, depending on your code you may need much more coarse grained locking, where the first loop protects both sentPings
and pingCounter
in one lock
, and maybe even the second method is completely protected with a lock
.
People can say not to use lock
as it causes performance issues, and lock free is very trendy. The bottom line is lock
is simpler than other alternatives most of the time. You may need to make your locking more coarse grained than the above sample because you potentially have race conditions as well. It's hard to give a better sample without seeing your entire program.
Interlocked.Increment
The main reason to use lock
here is to force each read and write to come from memory, not CPU cache, and therefore you should get consistent values. An alternative to lock is to use Interlocked.Increment, but if you use that on two separate variables you need to watch carefully for race conditions.
(Edit)
Even if you lock you potentially have a problem. Watch this timeline for 13 target addresses (unlucky for some). If you aren't comfortable with why this is, then have a look at "Managed Threading Basics" and "Threading in C# - Joseph Albahari"
sentPings++
sentPings++
(now equal to 13)recievedIpAddresses == sentPings
test - now fails as they are not equalpingCompleted
and does recievedIpAddresses++;
You need to watch carefully for this type of race condition in your code, and adjust it accordingly. The whole thing about threads is that they overlap their operations.
Footnote:
Why is SyncRoot
declared as: private readonly object SyncRoot = new object();
?
static
console app, it will need to be static
. But, if you use static
in a class, then every instance will lock on the same object, so there will be contentionreadonly
to declare intent, and prevent you (or other team members) overwriting it laterobject
as:
SyncRoot
as an example; Visual Studio has historically called it that in it's snippetsIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With