Reputation: 13
I have a PowerShell script that checks if a file is present in a folder. The problem is: This script works as it should, but it's very slowly. I must check 10K Pcs for a statistic / day.
I want to use Invoke-Command
, but I can't use it because not all clients have enabled WinRM.
Is it possible to make this script multithread without WinRM?
Here is my code:
function Show-Menu {
param (
[string]$Title = 'Debug'
)
cls
Write-Host "================ $Title ================"
Write-Host "Ziel Clients werden in der Datei C:\temp\srv.txt angegeben!" -ForegroundColor Red
Write-Host "1: Detailansicht alle Clients" -ForegroundColor Green
Write-Host "2: Today Crash" -ForegroundColor Green
Write-Host "3: Detailansich einzelner Client" -ForegroundColor Green
Write-Host "Q: 'Q' zum beenden."
Write-Host "Script wird ausgefuehrt als:"
whoami
}
do {
Show-Menu
$input = Read-Host "Nummer angeben"
switch ($input) {
'1' {
cls
Write-Host "Detailansicht alle Clients"
$computers = Get-Content C:\temp\srv.txt
foreach ($computer in $computers) {
Write-Host -foregroundcolor "green" "Verarbeite $computer..."
if ( ! (Test-Connection $computer -Count 1 -Quiet)) {
Write-Host -foregroundcolor "red" "$computer ist offline"
continue
}
$path = Test-Path "\\$computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*" -Include *dump*
Get-Item "\\$computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*"
If ($path -eq $true ) { Write-Host $computer 'Dumps are present' }
Else { Write-Host $computer 'Dumps are not present' }
pause
}
}
'2' {
cls
Write-Host "Today Crash"
$computers = Get-Content C:\temp\srv.txt
foreach ($computer in $computers) {
Write-Host -foregroundcolor "green" "Verarbeite $computer..."
if ( ! (Test-Connection $computer -Count 1 -Quiet)) {
Write-Host -foregroundcolor "red" "$computer ist offline"
continue
}
$result = Get-ChildItem -Path "\\$computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*" | Where-Object { $_.LastWriteTime -ge (Get-Date).Date }
}
$result | Out-GridView
}
'3' {
cls
Write-Host "Detailansich einzelner Client"
$computer = Read-Host -Prompt 'Client angeben'
$result = Get-ChildItem -Path "\\$computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*"
$result | Out-GridView
}
}
}
until ($input -eq 'q')
Upvotes: 1
Views: 4922
Reputation: 440247
While background jobs - started via Start-Job
- do permit running in parallel, they run in child processes, and are therefore both resource-intensive and slow; furthermore, you cannot throttle their use, i.e. you cannot (directly) control how many child process at most are permitted to run simultaneously
Thread jobs, by contrast, run as threads in-process and therefore require fewer resources and are much faster than child-process-based background jobs; furthermore, throttling (limiting the number of threads permitted to run simultaneously) is supported.
Thread jobs are started via the Start-ThreadJob
cmdlet, which comes with PowerShell [Core] v6+ and in Windows PowerShell can be installed on demand with, e.g., Install-Module ThreadJob -Scope CurrentUser
.
You simply call Start-ThreadJob
instead of Start-Job
, and use the standard *-Job
cmdlets to manage such thread jobs - the same way you'd manage a Start-Job
-launched background job.
In the simplest case, you can simply wait for all threads to complete:
# PowerShell [Core] 6+
# Windows PowerShell with the ThreadJob module installed.
Get-Content C:\temp\srv.txt | ForEach-Object {
$computer = $_
Start-ThreadJob { # create a thread job and output an object representing it
Write-Host -ForegroundColor Green "Processing $using:computer..."
# ...
Get-Item "\\$using:computer\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*"
# ...
}
} | Receive-Job -Wait -AutoRemoveJob
Note the use of the $using:
scope specifier, which is needed to access the caller's $computer
value; this requirement applies equally to background jobs, thread jobs, and remoting.
By default, up to 5 threads are allowed to run simultaneously; you can use the -ThrottleLimit
parameter to modify this value, but note that increasing this value beyond what your hardware can support can actually slow things down.
Output sequencing isn't guaranteed; that is, the outputs aren't guaranteed to correspond to the order in which the computer names were specified.
In PowerShell 7+, there's an even easier way to run threads in parallel, via ForEach-Object
's
-Parallel
parameter:
# PowerShell 7+
Get-Content C:\temp\srv.txt | ForEach-Object -Parallel {
Write-Host -foregroundcolor Green "Processing $_..."
# ...
Get-Item "\\$_\c$\Program Files\Oracle\Runtime\BIN\ifrun60_*"
# ...
}
The comments above regarding output sequencing and throttling apply equally here, but note that the computer names are now provided as input via the pipeline, so the usual automatic $_
variable can be used inside the thread script block.
Upvotes: 3