Problem using BackGroundWorker to ping multiple LAN hosts

  • Thread starter Thread starter Michael M.
  • Start date Start date
M

Michael M.

I have the following code (listed at bottom of post) that pings a small
range of IP address to see which ones are alive.

To speed things up a little I am trying to use more than one thread, problem
is instead of returning:



192.168.0.1 online

192.168.0.2 offline

192.168.0.3 online

192.168.0.4 offline

It seems to return this (like the IPHostOctet is not incrementing before I
fire of the worker processes)

It is almost like the the Argument is passed "by ref" meaning by the time
the first thread fires it has been incremened 4 times and holds that value
the same value in each bgworker / ping object.

or mabye it only gets incremened once per loop? It is declred outside the
procedure and is of type Integer



FYI: inside the do work methods of the Background workers it just appends
the incrmeting network host byte / octect like:



reply = Pinger.Ping (192.168.0. & IpHostOctect.tostring)



192.168.0.1 offline

192.168.0.1 offline

192.168.0.1 offline

192.168.0.1 offline

192.168.0.2 offline

192.168.0.2 offline





The code that calls the Background workers (it is within a loop in the
actual program)

If bgwPinger.IsBusy = False Then

IpHostOctect += 1

bgwPinger.RunWorkerAsync(IpHostOctect)

End If

If bgwPinger2.IsBusy = False Then

IpHostOctect += 1

bgwPinger2.RunWorkerAsync(IpHostOctect)

End If

If bgwPinger3.IsBusy = False Then

IpHostOctect += 1

bgwPinger3.RunWorkerAsync(IpHostOctect)

End If



If bgwPinger4.IsBusy = False Then

IpHostOctect += 1

bgwPinger4.RunWorkerAsync(IpHostOctect)



End If
 
Michael said:
I have the following code (listed at bottom of post) that pings a small
range of IP address to see which ones are alive.

To speed things up a little I am trying to use more than one thread, problem
is instead of returning:

I was curious about your problem so I copied your code exactly and found
that unless I inserted -

System.Windows.Forms.Application.DoEvents()

after each call the the BackGround Worker, the BGW would only run once
and then my application would appear to hang.

Once the DoEvents was inserted, everything worked perfectly.

By the way, in each of my BGW Subs I used the following code (watch for
wrapping) -

Dim Ping As New Ping
Dim PingReply As PingReply = Ping.Send("192.168.1." & e.Argument.ToString)
Debug.Print(String.Format("192.168.1.{0} {1}", e.Argument.ToString,
IIf(PingReply.Status = IPStatus.Success, "Online", "Offline")))


Hope this helps.

ShaneO

There are 10 kinds of people - Those who understand Binary and those who
don't.
 
ShaneO said:
I was curious about your problem so I copied your code exactly and found
that unless I inserted -

Dim PingReply As PingReply = Ping.Send("192.168.1." & e.Argument.ToString)
Debug.Print(String.Format("192.168.1.{0} {1}", e.Argument.ToString,
IIf(PingReply.Status = IPStatus.Success, "Online", "Offline")))

By the way, I found if I used the "TimeOut" value in my Ping call, as in
the following -

Dim PingReply As PingReply = Ping.Send("192.168.1." &
e.Argument.ToString, 200)

the code would run very quickly as the default TimeOut value obviously
allows too much time. Setting this value too low (<100) did not always
allow enough time for each computer to respond, so I would obtain false
"Offline" results.


ShaneO

There are 10 kinds of people - Those who understand Binary and those who
don't.
 
Michael said:
I have the following code (listed at bottom of post) that pings a small
range of IP address to see which ones are alive.

To speed things up a little I am trying to use more than one thread, problem
is instead of returning:
It is almost like the the Argument is passed "by ref" meaning by the time
the first thread fires it has been incremened 4 times and holds that value
the same value in each bgworker / ping object.

or mabye it only gets incremened once per loop? It is declred outside the
procedure and is of type Integer
The code that calls the Background workers (it is within a loop in the
actual program)

If bgwPinger.IsBusy = False Then

IpHostOctect += 1

bgwPinger.RunWorkerAsync(IpHostOctect)

End If

If bgwPinger2.IsBusy = False Then

IpHostOctect += 1
<snip>

This is not the way to do it, as you may have guessed. Calling the
BGWorkers in a loop that waits 'til they're not busy is a tremendous
waste of resources and will simply freeze your UI, as ShaneO has
showed you. You'd get better by simply calling Ping.Send() in the
loop, instead...

I suggest you ditch the loop and focus on synchronizing the calls to
the BGWorkers. It's way more work, yes, but will give you the kind of
response you expect (I guess). Something in the lines of:

<aircode>
'At form level, the current octet:
Private mHostOctet As Byte

Private Sub WorkersWork( _
Sender As Object, _
E As DoWorkEventArgs _
) Handles bgwPinger1.DoWork, _
bgwPinger2.DoWork, _
bgwPinger3.DoWork, _
bgwPinger4.DoWork

'This issues a ping in another thread.

Dim Pinger As New Ping
Dim HostOctet As Byte = CInt(E.Argument) And 255
Dim Reply As PingReply = Pinger.Send( _
STR_BASEADDR & HostOctet.ToString)

E.Result = Reply
End Sub

Private Sub WorkersDone( _
Sender As Object, _
E As RunWorkerCompletedEventArgs _
) Handles bgwPinger1.RunWorkerCompleted, _
bgwPinger2.RunWorkerCompleted, _
bgwPinger3.RunWorkerCompleted, _
bgwPinger4.RunWorkerCompleted

'This is called in the main thread
'when each worker finishes

Dim Reply As PingReply = CType(E.Result, PingReply)

'Do Whatever you want with the reply

'Continues pinging with this bgworker
Dim ThisWorker As BackgroundWorker = _
CType(Sender, BackgroundWorker)
PingNext(ThisWorker)

End Sub

Private Sub PingNext(Worker As BackgroundWorker)
'Calls the aupplied worker with the current value
'of the host octet and increments the octet, so the
'next worker pings another address

If mHostOctet < 255 then
Worker.RunWorkerAsync(mHostOctet)
mHostOctet += 1
End If
End Sub
</aircode>

And in the method that you used to activate the "pingers", you could
do something like this:

<aircode>
'Start the first octet:
mHostOctet = 1
PingNext(bgwPinger1)
PingNext(bgwPinger2)
PingNext(bgwPinger3)
PingNext(bgwPinger4)
'that's it.
</aircode>

Everytime a worker finishes, the WorkersDone method will execute (in
the main thread) where you can use the reply passed in the E.Result to
update the UI.

One more thing: If you go multithread (and I think you will) remember
that the Ping class already has a method that will ping in a separate
thread, so you don't really need the BGWorkers...

HTH.

Regards,

Branco.
 
Thanks ShaneO,

You are right the code does work. It worked correctly when I pasted it in
to a new project.

This was such a strange problem, It had something to do with the
IDE/Complier caching something, I was changing my code but the changes would
never be reflected when compiled / run until I deleted the contents of the
obj folder that gets created every time you compile a project.

It got as bad as the fact it was running a BGW do work SUB that no longer
exsited in the project.

As far as I know the Application.Doevents should be called within any long
loop that is executed on the GUI thread to keep it responsive.

Regards,

Mike
 
Branco,

I was just trying to keep things simple in the explanation.

The "loop" is realy a timer object, but yes it's not the best option (I was
never realy happy with the timer loop executing and not having any thing to
do (when all the threads are busy)).

I will consider your code / way of using background workers (I have never
used them before).

Thanks for taking the time to write the example code.

I should have explained this better.

Mike.
 
Back
Top