SendPingAsync makes form freeze

Ask questions about creating Graphical User Interfaces (GUI) in PowerShell and using WinForms controls.
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
User avatar
itbiodk
Posts: 8
Joined: Sun Apr 23, 2017 1:44 pm

SendPingAsync makes form freeze

Post by itbiodk » Sat Jul 07, 2018 1:27 pm

Hello,
I have made a GUI ping tool for mass pinging of a range of IP-addresses.
I have tried the tool with a test-connection -asjob function - it works, but it seems to have a problem with big ranges (more than 5000 addresses).

I am now interested in trying SendPingAsync. The function works great by itself in ISE and obviously normal PS, but when I run it in the GUI through Powershell Studio or ISE, it gets stuck.
I have run the debug and the threading tasks is input into the variable as a bunch of System.Threading.Tasks.Task`1[System.Net.NetworkInformation.PingReply].
So far, so good... but then it gets stuck and the result is never pulled out. It seems the threading task never finishes.
Is it a limitation with winforms ? or maybe just lack of knowledge on my part?

Here is the fuction and after that how you can run it:

Code: Select all

function Ping-Fast
{
	param ($servers)
	$i = 0
	$results = New-Object 'System.Collections.Generic.List[System.Object]'
	
	$PingAsync = foreach ($server in $servers)
	{
		(New-Object Net.NetworkInformation.Ping).SendPingAsync($server,250)
	}
	[System.Threading.Tasks.Task]::WaitAll($PingAsync) 
	
	foreach ($result in $PingAsync.result)
	{
		if ($result.Address-like "0.0.0.0")
		{
			$address = $servers[$i]
			$Reachable = "NO"
		}
		else
		{
			$address = $result.address
			$Reachable = "YES"
		}
		$obj = New-Object -TypeName PSObject
		$obj | Add-Member -MemberType NoteProperty -Name "Nr" -Value $i
		$obj | Add-Member -MemberType NoteProperty -Name "Address" -Value $address
		$obj | Add-Member -MemberType NoteProperty -Name "Reachable" -Value $reachable
		
		$results.add($obj)
		
		$i++		
	}
	return $results
}
Here is how you can test it:

Code: Select all

$ipsLastOctet = @(1..254)
    $ips = New-Object 'System.Collections.Generic.List[System.Object]'
    foreach ( $ipLastOctet in $ipsLastOctet )
    {
    $ips.add("192.168.0.$ipLastOctet")
    }
    Ping-Fast -servers $ips 
Edit: For those who are interested, I did a test in ISE between the two functions. I pinged a /16 network (65534 addresses) where I know only 3 addresses responded.
  • Test-connection -asjob:
Result: Never finished. It stopped after a few minutes and I don't know how far it got.
CPU: Mostly used around 25%-35% but went to 100% usage at times.
RAM: It stopped processing when ISE was at 2088 MB usage. Didn't rise or fall after that.
  • SendPingAsync:
Result: First test finished at 1 minute and 37 seconds. Second test finished at 2 minutes and 10 seconds. All 3 addresses were found both times.
Did a test without the timeout value of 250 and it finished at 1 minute and 49 seconds.
CPU: Mostly at 10%-13% but peaked at 17%.
RAM: Mostly at around 180-200 MB usage but peaked at around 250 MB usage.

That is a huge difference and why I am so very interested in switching the functions.

Edit Edit: I have attached a small test form with the function implemented. It will just post the output with out-gridview but it demonstrates the problem.
It uses BarryCWT's excellent get-iprange function to get the range of IP-addresses to send to the ping function ( https://gallery.technet.microsoft.com/s ... a-60c5bb6b ).
Currently is is set for a /30 network (for ease of debugging) but you can set the range yourself in the $buttonPing_Click event scriptblock.
Attachments
PingFormTest.psf
(98.36 KiB) Downloaded 4 times
Last edited by itbiodk on Sat Jul 07, 2018 2:42 pm, edited 1 time in total.

User avatar
jvierra
Posts: 12676
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: SendPingAsync makes form freeze

Post by jvierra » Sat Jul 07, 2018 2:37 pm

All of what you have coded is built into PowerSHell. PS also manages this much better and easier than hand coding. It also manages memory correctly. You an tune the parallel tasks easily to determing how many tasks are created and it reuses completed task to conserver memory. Memory is released correctly when the WF completes.

Most of you issues are continuous creating of objects even though it is unlikely that you will have any more than about 10 or fewer tasks executing at any one time. Workflow uses a TaskFactory that is separate from the basic PowerShell runspace as it is executed by the system WorkFlow engine.

Code: Select all

workflow Send-ParallelPing{
    Param($servers)
    foreach -parallel ($server in $servers){
        if(Test-Connection $server -Quiet -Count 1 -TimeToLive 250){
            [pscustomobject]@{
                Server = $server
                Status = 'Success'
            }
        }else{
            [pscustomobject]@{
                Server = $server
                Status = 'Failed'
            }
        }
    }
}

Send-ParallelPing $servers | select server,status

User avatar
itbiodk
Posts: 8
Joined: Sun Apr 23, 2017 1:44 pm

Re: SendPingAsync makes form freeze

Post by itbiodk » Sat Jul 07, 2018 3:36 pm

jvierra wrote:
Sat Jul 07, 2018 2:37 pm
All of what you have coded is built into PowerSHell. PS also manages this much better and easier than hand coding. It also manages memory correctly. You an tune the parallel tasks easily to determing how many tasks are created and it reuses completed task to conserver memory. Memory is released correctly when the WF completes.

Most of you issues are continuous creating of objects even though it is unlikely that you will have any more than about 10 or fewer tasks executing at any one time. Workflow uses a TaskFactory that is separate from the basic PowerShell runspace as it is executed by the system WorkFlow engine.

Code: Select all

workflow Send-ParallelPing{
    Param($servers)
    foreach -parallel ($server in $servers){
        if(Test-Connection $server -Quiet -Count 1 -TimeToLive 250){
            [pscustomobject]@{
                Server = $server
                Status = 'Success'
            }
        }else{
            [pscustomobject]@{
                Server = $server
                Status = 'Failed'
            }
        }
    }
}

Send-ParallelPing $servers | select server,status
Hey and thanks for taking the time to answer.
I tried your function and I found a couple of things.
1. The -TimeToLive parameter for Test-connection is in seconds. I changed that to the smallest, which is 1.
2. I am sorry to say that the time it takes to execute is very bad. I tried a /24 subnet and it took 4 minutes and 28 seconds. That is more than twice as long as SendPingAsync takes to do a /16 subnet. A /24 subnet takes it 4.7 seconds. If there is a way to speed it up, that would be great.
3. I only have one issue and while I certainly am no master at PS, I don't see continuous creation of objects being a problem here when first of all, your function also creates objects(mine creates more, I know but it has never been a problem for me in the past) and secondly the issue is the same whether it is 1 IP or 65000 IP's, and finally that it works perfect as just a script but not in GUI.

Sorry if I come of as aggressive and I might be horribly wrong, but then I look forward to learning something new.

User avatar
jvierra
Posts: 12676
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: SendPingAsync makes form freeze

Post by jvierra » Sat Jul 07, 2018 4:12 pm

The main thing to remember is that it works.

Now change the number of threads released to a larger number and it will improve performance.

You will find that async waiting in PwoerShell can cause issues as PS is not natively multi-threaded. The PS session sits on a single thread. If you further try to put this in a form which already has the main thread blocked it will fail.

You can use the task factory to control the thread allocation and run this all in a separate runspace and it should work with no issues.

The correct method for sync calls is t use a callback that gets the thread results and releases the thread as threads complete.

You can also pass an object to the thread creation and (SendPingAsync) and use the object to track the thread status.

User avatar
jvierra
Posts: 12676
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: SendPingAsync makes form freeze

Post by jvierra » Sat Jul 07, 2018 4:28 pm


User avatar
jvierra
Posts: 12676
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: SendPingAsync makes form freeze

Post by jvierra » Sat Jul 07, 2018 4:47 pm

This will likely be faster and easier to manage. It also avoids async issues with PS. THe ping object can be moe easily tailored using PingOptions.

Code: Select all

workflow Send-ParallelPing{
    Param($servers)
    foreach -parallel  -throttlelimit 64 ($server in $servers){
        Try{
            $P = [System.Net.NetworkInformation.Ping]::new()
            $result = $P.Send($server)
            [pscustomobject]@{
                Server = $server
                Status = 'Success'
                PingStatus = $result.Status
                Exception = $null
            }
        }
        Catch{
            [pscustomobject]@{
                Server = $server
                Status = 'Failed'
                PingStatus = $null
                Exception = $_
            }
        }
    }
}

Send-ParallelPing $servers | select server,status

User avatar
jvierra
Posts: 12676
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: SendPingAsync makes form freeze

Post by jvierra » Sat Jul 07, 2018 4:53 pm

The following should be a bit faster and it will return results as each thread completes.

Code: Select all


workflow Send-ParallelPing{
    Param($servers)
    foreach -parallel -throttlelimit 64 ($server in $servers){
        InlineScript {
            Try{
                $P = [System.Net.NetworkInformation.Ping]::new()
                $result = $P.Send($using:server)
                [pscustomobject]@{
                    Server = $using:server
                    Status = 'Success'
                    Address = $result.Address
                    PingStatus = $result.Status
                    Exception = $null
                }
                $p.Dispose()
            }
            Catch{
                [pscustomobject]@{
                    Server = $using:server
                    Status = 'Failed'
                    Address = $null
                    PingStatus = $null
                    Exception = $_
                }
            }
        }
    }
}

Send-ParallelPing $servers

User avatar
jvierra
Posts: 12676
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: SendPingAsync makes form freeze

Post by jvierra » Sat Jul 07, 2018 8:02 pm

A little research. TTL or TImeToLive is the number of hops before setting a return status. TTL less than 5 will not be normally honored as the ping will complete even in fewer hops and will not terminate at the fifth hop.
Only the "TimeOut" on the send will terminate the wait at a specific time (see docs for this).

Use "Send" with a timeout. With a workflow the timeout is not necessary since the threads are running in parallel.

The documentation for Test-Connection and for "Ping" have been rewritten a couple of times and each the docs get worse. Most Net classes have serious errors in the documentation which is a result of the new community documentation effort. The older docs were better but still suffered from errors due to the lack of training in networking by the writers.

User avatar
jvierra
Posts: 12676
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: SendPingAsync makes form freeze

Post by jvierra » Sat Jul 07, 2018 9:50 pm

The tasks are faster but will not work in a form. You can try to use a job and monitor the job for completion.

Just tested. It works in a form if it is run in job. A 'C' subnet takes about 2 seconds.

User avatar
itbiodk
Posts: 8
Joined: Sun Apr 23, 2017 1:44 pm

Re: SendPingAsync makes form freeze

Post by itbiodk » Sun Jul 08, 2018 7:22 am

THANK YOU! for your very generous help.
I went with the last option of wrapping it in a job, but I am interested in workflows and parallel processing now too. I just need to learn a little bit first.

I agree that the documentation for some of the .NET functions and Posh commands/fuctions are very confusing and/or just badly documented.
You really provided me with some good insight here and I appreciate it.

Locked