Do you have any kind of lock at all? This seems like your problem to me. I see in your code all kinds of race conditions and problems with the memory processor cache .
Try using lock to protect recievedIpAddresses == sentPings and
sentPings++;
Using 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) {
The above example will force you to read and write data from shared memory so that different CPU cores do not read different values. However, depending on your code, you may need a much coarse-grained lock, where the first loop protects both sentPings and pingCounter in one lock , and perhaps even the second method is fully protected by lock .
People may say that do not use lock , as it causes performance problems, and locking is very fashionable. The bottom line of lock in most cases simpler than other alternatives. You may need to make your lock more coarse than the above sample, because you also have race conditions. Itβs hard to give a better sample without seeing your entire program.
Interlocked.Increment
The main reason for using lock here is to force every read and write to come from memory, not from the CPU cache, and so you should get consistent values. An alternative to locking is to use Interlocked.Increment , but if you use this on two separate variables, you need to carefully monitor the race conditions.
Race conditions
(Edit)
Even if you block, you may have a problem. Check out this timeline for 13 destination addresses (unfortunately for some). If you are uncomfortable with why this is the case, take a look at Managed Threading Basics and Threading in C # - Joseph Albahari
- T1: 1 time
- T1: Ping sent
- T1:
sentPings++
- T2: 1 time
- recievedIpAddresses ++;
- T2: other things
- Meanwhile T1: 12 times
- T1: Ping sent
- T1:
sentPings++ (now equal to 13)
- T2:
recievedIpAddresses == sentPings test - now fails because they are not equal - T3 to T14:
pingCompleted enters and does recievedIpAddresses++; - T1 ends, the application gets the opportunity to record ping counts (or, even worse, completely exits) before the remaining 12 threads return in the background
You need to carefully monitor this type of race condition in your code and adjust it accordingly. The thing about flows is that they overlap their operations.
Syncroot
Footnote:
Why is SyncRoot declared as: private readonly object SyncRoot = new object(); ?
- This is a class field to protect class fields, if you have a
static console application, it should be static . But, if you use static in the class, then each instance will block the same object, so there will be conflicts readonly declare intent and stop you (or other team members) from rewriting it later- This is an
object like:- you need nothing but an object
- you cannot block value type
- you should not lock your class instance (to prevent deadlocks in more complex code)
- you should not publish it (also to prevent deadlocks)
- It is created together with the class (safe thread) using this operator
- It is called
SyncRoot as an example; Visual Studio historically called it fragments in it