Convert to multithreading

I am a sysadmin for my network, but we are in a tiered enterprise domain. Our McAfee ePO server is controlled above us, and they are holding on to all permissions (job security). So I came up with the below script to parse through a list of computers and get their McAfee Dat version and write it to a text file.

When I run it across 3000 machines, it takes a long time. So I was hoping someone could help me convert it to multithreading.

    $ErrorActionPreference = "silentlycontinue"
filter Check-Online {
    trap { continue }
    . {
      $timeout = 2000
      $obj = New-Object system.Net.NetworkInformation.Ping
      $result = $obj.Send($_, $timeout)
      if ($result.status -eq 'Success') {1 }
    }
}
function Get-WebPage {
 
[cmdletbinding(
    DefaultParameterSetName = 'url',
    ConfirmImpact = 'low'
)]
    Param(
        [Parameter(
            Mandatory = $True,
            Position = 0,
            ParameterSetName = '',
            ValueFromPipeline = $True)]
            [string][ValidatePattern("^(http|https)\://*")]$Url,
        [Parameter(
            Position = 1,
            Mandatory = $False,
            ParameterSetName = 'defaultcred')]
            [switch]$UseDefaultCredentials,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = '')]
            [string]$Proxy,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = 'altcred')]
            [switch]$Credential,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = '')]
            [switch]$ShowSize                        

        )
Begin {     
    $psBoundParameters.GetEnumerator() | % { 
        Write-Verbose "Parameter: $_" 
        }

    #Create the initial WebClient object
    Write-Verbose "Creating web client object"
    $wc = New-Object Net.WebClient 

    #Use Proxy address if specified
    If ($PSBoundParameters.ContainsKey('Proxy')) {
        #Create Proxy Address for Web Request
        Write-Verbose "Creating proxy address and adding into Web Request"
        $wc.Proxy = New-Object -TypeName Net.WebProxy($proxy,$True)
        }       

    #Determine if using Default Credentials
    If ($PSBoundParameters.ContainsKey('UseDefaultCredentials')) {
        #Set to True, otherwise remains False
        Write-Verbose "Using Default Credentials"
        $wc.UseDefaultCredentials = $True
        }
    #Determine if using Alternate Credentials
    If ($PSBoundParameters.ContainsKey('Credentials')) {
        #Prompt for alternate credentals
        Write-Verbose "Prompt for alternate credentials"
        $wc.Credential = (Get-Credential).GetNetworkCredential()
        }         

    }
Process {    
    Try {
        If ($ShowSize) {
            #Get the size of the webpage
            Write-Verbose "Downloading web page and determining size"
            "{0:N0}" -f ($wr.DownloadString($url) | Out-String).length -as [INT]
            }
        Else {
            #Get the contents of the webpage
            Write-Verbose "Downloading web page and displaying source code" 
            $wc.DownloadString($url)       
            }

        }
    Catch {
        Write-Warning "$($Error[0])"
        }
    }   
}  

Function Get-RemoteRegistry {
    #This Function is read remote registry
    param(
        [string]$computer = $(Read-Host "Remote Computer Name")
       ,[string]$Path     = $(Read-Host "Remote Registry Path (must start with HKLM,HKCU,etc)")
       ,[string[]]$Properties
       ,[switch]$Verbose
    )
    if ($Verbose) { $VerbosePreference = 2 } # Only affects this script.

       $root, $last = $Path.Split("\")
       $last = $last[-1]
       $Path = $Path.Substring($root.Length + 1,$Path.Length - ( $last.Length + $root.Length + 2))
       $root = $root.TrimEnd(":")

       #split the path to get a list of subkeys that we will need to access
       # ClassesRoot, CurrentUser, LocalMachine, Users, PerformanceData, CurrentConfig, DynData
       switch($root) {
          "HKLM"  { $root = "LocalMachine" }
          default { return "Path argument is not valid" }
       }


       #Access Remote Registry Key using the static OpenRemoteBaseKey method.
       Write-Verbose "Accessing $root from $computer"
       #Add-Content $loglocation "Accessing $root from $computer"
       $rootkey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($root,$computer)
       if(-not $rootkey) { Write-Error "Can't open the remote $root registry hive" }

       Write-Verbose "Opening $Path"
       #Add-Content $loglocation "Opening $Path"
       $key = $rootkey.OpenSubKey( $Path )
       if(-not $key) { 
       Write-Error "Can't open $($root + '\' + $Path) on $computer"
       #Add-Content $loglocation "Can't open $($root + '\' + $Path) on $computer"
       }

       $subkey = $key.OpenSubKey( $last )

       $output = new-object object

       if($subkey -and $Properties -and $Properties.Count) {
          foreach($property in $Properties) {
             Add-Member -InputObject $output -Type NoteProperty -Name $property -Value $subkey.GetValue($property)
          }
          Write-Output $output
       } elseif($subkey) {
          Add-Member -InputObject $output -Type NoteProperty -Name "Subkeys" -Value @($subkey.GetSubKeyNames())
          foreach($property in $subkey.GetValueNames()) {
             Add-Member -InputObject $output -Type NoteProperty -Name $property -Value $subkey.GetValue($property)
          }
          Write-Output $output
       }
       else
       {
          $key.GetValue($last)
       }
}


$loglocation = Read-Host "Please input a local path to save the logs to. Ex: C:\mylog.txt"
$Computerlist = Read-Host "Please input the path to local file that contains the computer's names. Ex: C:\computernames.txt"

$webpage = Get-WebPage -url "ftp://ftp.mcafee.com/commonupdater/"
$val1 = $webpage.indexof("avvdat-") + 4
$val2 = $webpage.indexof(".zip")
$output = $webpage.substring($val1,$val2-$val1)
$output = $output.replace("at-", "")
write-host "The current DAT file version is: $output"
Add-Content $loglocation "The current DAT file version is: $output"

$servers = get-content $Computerlist #read the computers from the text file

foreach($s in $servers){ #This foreach loops through the text file of computers and performs the below actions on each computer.
    #Check if they are online
    if (($s | Check-Online) -eq 1) {     
        write-host "$s is online"
        $Regvalue = Get-RemoteRegistry $s "HKLM\SOFTWARE\Wow6432Node\McAfee\AVEngine\AVDatDate"     
        $Regvalue2 = Get-RemoteRegistry $s "HKLM\SOFTWARE\Wow6432Node\McAfee\AVEngine\AVDatVersion"     
        $Regvalue3 = Get-RemoteRegistry $s "HKLM\SOFTWARE\McAfee\AVEngine\AVDatDate"        
        $Regvalue4 = Get-RemoteRegistry $s "HKLM\SOFTWARE\McAfee\AVEngine\AVDatVersion" 

        $aValue = $Regvalue
        $bValue = $Regvalue2
        if($Regvalue -eq $null){
            $aValue = $Regvalue3
        }

        if($Regvalue2 -eq $null){
            $bValue = $Regvalue4
        }
        write-host $Regvalue
        write-host $Regvalue2
        write-host $Regvalue3
        write-host $Regvalue4
        Add-Content $loglocation "$s,$aValue,$bValue"
    }else{
        Write-Error "$s is offline"
    }
}

Maybe you should take a closer look at powershell jobs.

Take a look at this documentation:
http://technet.microsoft.com/en-us/library/hh849698.aspx

I tried but it didn’t work

$ErrorActionPreference = "silentlycontinue"
filter Check-Online {
	trap { continue }
	. {
	  $timeout = 2000
	  $obj = New-Object system.Net.NetworkInformation.Ping
	  $result = $obj.Send($_, $timeout)
	  if ($result.status -eq 'Success') {1 }
	}
}
function Get-WebPage {
 
[cmdletbinding(
	DefaultParameterSetName = 'url',
	ConfirmImpact = 'low'
)]
    Param(
        [Parameter(
            Mandatory = $True,
            Position = 0,
            ParameterSetName = '',
            ValueFromPipeline = $True)]
            [string][ValidatePattern("^(http|https)\://*")]$Url,
        [Parameter(
            Position = 1,
            Mandatory = $False,
            ParameterSetName = 'defaultcred')]
            [switch]$UseDefaultCredentials,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = '')]
            [string]$Proxy,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = 'altcred')]
            [switch]$Credential,
        [Parameter(
            Mandatory = $False,
            ParameterSetName = '')]
            [switch]$ShowSize                        
                        
        )
Begin {     
    $psBoundParameters.GetEnumerator() | % { 
        Write-Verbose "Parameter: $_" 
        }
   
    #Create the initial WebClient object
    Write-Verbose "Creating web client object"
    $wc = New-Object Net.WebClient 
    
    #Use Proxy address if specified
    If ($PSBoundParameters.ContainsKey('Proxy')) {
        #Create Proxy Address for Web Request
        Write-Verbose "Creating proxy address and adding into Web Request"
        $wc.Proxy = New-Object -TypeName Net.WebProxy($proxy,$True)
        }       
    
    #Determine if using Default Credentials
    If ($PSBoundParameters.ContainsKey('UseDefaultCredentials')) {
        #Set to True, otherwise remains False
        Write-Verbose "Using Default Credentials"
        $wc.UseDefaultCredentials = $True
        }
    #Determine if using Alternate Credentials
    If ($PSBoundParameters.ContainsKey('Credentials')) {
        #Prompt for alternate credentals
        Write-Verbose "Prompt for alternate credentials"
        $wc.Credential = (Get-Credential).GetNetworkCredential()
        }         
        
    }
Process {    
    Try {
        If ($ShowSize) {
            #Get the size of the webpage
            Write-Verbose "Downloading web page and determining size"
            "{0:N0}" -f ($wr.DownloadString($url) | Out-String).length -as [INT]
            }
        Else {
            #Get the contents of the webpage
            Write-Verbose "Downloading web page and displaying source code" 
            $wc.DownloadString($url)       
            }
        
        }
    Catch {
        Write-Warning "$($Error[0])"
        }
    }   
}  



Function Get-RemoteRegistry {
	#This Function is read remote registry
    param(
        [string]$computer = $(Read-Host "Remote Computer Name")
       ,[string]$Path     = $(Read-Host "Remote Registry Path (must start with HKLM,HKCU,etc)")
       ,[string[]]$Properties
       ,[switch]$Verbose
    )
    if ($Verbose) { $VerbosePreference = 2 } # Only affects this script.
     
       $root, $last = $Path.Split("\")
       $last = $last[-1]
       $Path = $Path.Substring($root.Length + 1,$Path.Length - ( $last.Length + $root.Length + 2))
       $root = $root.TrimEnd(":")
     
       #split the path to get a list of subkeys that we will need to access
       # ClassesRoot, CurrentUser, LocalMachine, Users, PerformanceData, CurrentConfig, DynData
       switch($root) {
          "HKLM"  { $root = "LocalMachine" }
          default { return "Path argument is not valid" }
       }
     
     
       #Access Remote Registry Key using the static OpenRemoteBaseKey method.
       Write-Verbose "Accessing $root from $computer"
	   #Add-Content $loglocation "Accessing $root from $computer"
       $rootkey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($root,$computer)
       if(-not $rootkey) { Write-Error "Can't open the remote $root registry hive" }
     
       Write-Verbose "Opening $Path"
	   #Add-Content $loglocation "Opening $Path"
       $key = $rootkey.OpenSubKey( $Path )
       if(-not $key) { 
	   Write-Error "Can't open $($root + '\' + $Path) on $computer"
	   #Add-Content $loglocation "Can't open $($root + '\' + $Path) on $computer"
	   }
     
       $subkey = $key.OpenSubKey( $last )
       
       $output = new-object object
     
       if($subkey -and $Properties -and $Properties.Count) {
          foreach($property in $Properties) {
             Add-Member -InputObject $output -Type NoteProperty -Name $property -Value $subkey.GetValue($property)
          }
          Write-Output $output
       } elseif($subkey) {
          Add-Member -InputObject $output -Type NoteProperty -Name "Subkeys" -Value @($subkey.GetSubKeyNames())
          foreach($property in $subkey.GetValueNames()) {
             Add-Member -InputObject $output -Type NoteProperty -Name $property -Value $subkey.GetValue($property)
          }
          Write-Output $output
       }
       else
       {
          $key.GetValue($last)
       }
}














	 
	 
$loglocation = Read-Host "Please input a local path to save the logs to. Ex: C:\mylog.txt"
$Computerlist = Read-Host "Please input the path to local file that contains the computer's names. Ex: C:\computernames.txt"
	 
$webpage = Get-WebPage -url "ftp://ftp.mcafee.com/commonupdater/"
$val1 = $webpage.indexof("avvdat-") + 4
$val2 = $webpage.indexof(".zip")
$output = $webpage.substring($val1,$val2-$val1)
$output = $output.replace("at-", "")
write-host "The current DAT file version is: $output"
Add-Content $loglocation "The current DAT file version is: $output"
	 
$servers = get-content $Computerlist #read the computers from the text file
 
foreach($s in $servers){ #This foreach loops through the text file of computers and performs the below actions on each computer.
	$scriptblock={
        param($s)
        #Check if they are online
	    if (($s | Check-Online) -eq 1) {	 
            write-output "$s is online"
            $Regvalue = Get-RemoteRegistry $s "HKLM\SOFTWARE\Wow6432Node\McAfee\AVEngine\AVDatDate"		
	 	    $Regvalue2 = Get-RemoteRegistry $s "HKLM\SOFTWARE\Wow6432Node\McAfee\AVEngine\AVDatVersion"		
	 	    $Regvalue3 = Get-RemoteRegistry $s "HKLM\SOFTWARE\McAfee\AVEngine\AVDatDate"		
	 	    $Regvalue4 = Get-RemoteRegistry $s "HKLM\SOFTWARE\McAfee\AVEngine\AVDatVersion"	
		
		    $aValue = $Regvalue
		    $bValue = $Regvalue2
		    if($Regvalue -eq $null){
                $aValue = $Regvalue3
            }

            if($Regvalue2 -eq $null){
                $bValue = $Regvalue4
            }
            write-output "$s= $Regvalue"
            write-output "$s= $Regvalue2"
            write-output "$s= $Regvalue3"
            write-output "$s= $Regvalue4"
            Add-Content $loglocation "$s,$aValue,$bValue"
        }else{
            Write-Error "$s is offline"
        }    
    }
    start-job -name $s -scriptblock $scriptblock -argumentlist $s
}

while (get-job -state "Running")
{
    start-sleep 1   
} 
get-job | receive-job
get-job | remove-job

Long Path Tool seems to have nothing to do with what I am asking about

I’ve been marking his posts regarding this “Long Path Tool” on unrelated threads as Spam. Three so far in the last few minutes. Hopefully he’ll catch on.

This script http://gallery.technet.microsoft.com/Powershell-to-create-many-1b4f6f37 shows an example of using Start-job, Get-job, Receive-job, Remove-job to run a PS script in several concurrently-running threads.
Notice that on this example, the number of threads = the number of vLUNs on line 11