Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A PowerShell számos lehetőséget kínál a párhuzamos meghívások létrehozására.
-
Start-Job
Az egyes feladatokat külön folyamatban futtatja, mindegyikhez egy új PowerShell-példány tartozik. Sok esetben a lineáris hurok gyorsabb. Emellett a szerializálás és a deszerializálás korlátozhatja a visszaadott objektumok hasznosságát. Ez a parancs a PowerShell minden verziójában be van építve. -
Start-ThreadJob
a ThreadJob modulban található parancsmag. Ez a parancs PowerShell-futtatótereket használ a szálalapú feladatok létrehozásához és kezeléséhez. Ezek a feladatok kisebb súlyúak, mint a létrehozottStart-Job
feladatok, és elkerülik a folyamatközi szerializálás és deszerializálás által megkövetelt típushűség esetleges elvesztését. A ThreadJob modul a PowerShell 7-et és újabb verziót tartalmazza. Windows PowerShell 5.1 esetén ezt a modult a PowerShell-katalógusból telepítheti. - A PowerShell SDK System.Management.Automation.Runspaces névterével saját párhuzamos logikát hozhat létre. A
ForEach-Object -Parallel
ésStart-ThreadJob
is a PowerShell-futtatótereket használja a kód párhuzamos végrehajtásához. - A munkafolyamatok a Windows PowerShell 5.1 egyik funkciója. A munkafolyamatok nem érhetők el a PowerShell 7.0-s és újabb verzióiban. A munkafolyamatok a PowerShell-szkriptek egy speciális típusa, amely párhuzamosan futtatható. Hosszú ideig futó feladatokhoz tervezték, és szüneteltethetők és folytathatók. A munkafolyamatok nem ajánlottak az új fejlesztéshez. További információ: about_Workflows.
-
ForEach-Object -Parallel
A PowerShell 7.0-s és újabb verzióinak egyik funkciója. PéldáulStart-ThreadJob
PowerShell-runspace-eket használ a szálalapú feladatok létrehozásához és kezeléséhez. Ez a parancs csővezetékben való használatra készült.
Végrehajtási egyidejűség korlátozása
A szkriptek párhuzamos futtatása nem garantálja a jobb teljesítményt. Az alábbi forgatókönyvek például kihasználhatják a párhuzamos végrehajtás előnyeit:
- Nagy számítási igényű szkriptek többszálas többmagos processzorokon
- Azok a szkriptek, amelyek időt töltenek az eredményekre való várakozásra vagy a fájlműveletek elvégzésére, mindaddig, amíg ezek a műveletek nem tiltják egymást.
Fontos, hogy a párhuzamos végrehajtás többletterhelése és az elvégzett munka típusa között egyensúlyban legyen. Emellett a párhuzamosan futtatható meghívások számának is vannak korlátai.
A Start-ThreadJob
parancsok ForEach-Object -Parallel
paraméterrel rendelkeznek az egyszerre futó feladatok számának korlátozásához. Amikor több feladat elindul, várólistára kerülnek, és megvárják, amíg az aktuális feladatok száma a korlátozási határérték alá csökken. A PowerShell 7.1 verziótól kezdve ForEach-Object -Parallel
alapértelmezés szerint újrahasználja a runspace-készletben lévő runspace-eket. A ThrottleLimit paraméter beállítja a futtatótérkészlet méretét. Az alapértelmezett futtatótérkészlet mérete 5. A UseNewRunspace kapcsolóval továbbra is létrehozhat egy új futtatóteret minden iterációhoz.
A Start-Job
parancs nem rendelkezik ThrottleLimit paraméterrel. Egyszerre futó feladatok számát kell kezelnie.
Teljesítmény mérése
Az alábbi függvény Measure-Parallel
a következő párhuzamos végrehajtási módszerek sebességét hasonlítja össze:
Start-Job
– gyermek PowerShell-folyamatot hoz létre a színfalak mögöttStart-ThreadJob
- minden feladatot külön szálon futtatForEach-Object -Parallel
- minden feladatot külön szálon futtatStart-Process
- aszinkron módon meghív egy külső programotMegjegyzés:
Ennek a megközelítésnek csak akkor van értelme, ha a párhuzamos feladatok csak egy külső program egyetlen hívásából állnak, és nem powerShell-kódblokkot futtatnak. A kimenet rögzítésének egyetlen módja a fájlra való átirányítás.
function Measure-Parallel {
[CmdletBinding()]
param(
[ValidateRange(2, 2147483647)]
[int] $BatchSize = 5,
[ValidateSet('Job', 'ThreadJob', 'Process', 'ForEachParallel', 'All')]
[string[]] $Approach,
# pass a higher count to run multiple batches
[ValidateRange(2, 2147483647)]
[int] $JobCount = $BatchSize
)
$noForEachParallel = $PSVersionTable.PSVersion.Major -lt 7
$noStartThreadJob = -not (Get-Command -ErrorAction Ignore Start-ThreadJob)
# Translate the approach arguments into their corresponding hashtable keys (see below).
if ('All' -eq $Approach) { $Approach = 'Job', 'ThreadJob', 'Process', 'ForEachParallel' }
$approaches = $Approach.ForEach({
if ($_ -eq 'ForEachParallel') { 'ForEach-Object -Parallel' }
else { $_ -replace '^', 'Start-' }
})
if ($noStartThreadJob) {
if ($interactive -or $approaches -contains 'Start-ThreadJob') {
Write-Warning "Start-ThreadJob is not installed, omitting its test."
$approaches = $approaches.Where({ $_ -ne 'Start-ThreadJob' })
}
}
if ($noForEachParallel) {
if ($interactive -or $approaches -contains 'ForEach-Object -Parallel') {
Write-Warning 'ForEach-Object -Parallel require PowerShell v7+, omitting its test.'
$approaches = $approaches.Where({ $_ -ne 'ForEach-Object -Parallel' })
}
}
# Simulated input: Create 'f0.zip', 'f1'.zip', ... file names.
$zipFiles = 0..($JobCount - 1) -replace '^', 'f' -replace '$', '.zip'
# Sample executables to run - here, the native shell is called to simply
# echo the argument given.
$exe = if ($env:OS -eq 'Windows_NT') { 'cmd.exe' } else { 'sh' }
# The list of its arguments *as a single string* - use '{0}' as the placeholder
# for where the input object should go.
$exeArgList = if ($env:OS -eq 'Windows_NT') {
'/c "echo {0} > NUL:"'
} else {
'-c "echo {0} > /dev/null"'
}
# A hashtable with script blocks that implement the 3 approaches to parallelism.
$approachImpl = [ordered] @{}
# child-process-based job
$approachImpl['Start-Job'] = {
param([array] $batch)
$batch |
ForEach-Object {
Start-Job {
Invoke-Expression ($using:exe + ' ' + ($using:exeArgList -f $args[0]))
} -ArgumentList $_
} |
Receive-Job -Wait -AutoRemoveJob | Out-Null
}
# thread-based job - requires the ThreadJob module
if (-not $noStartThreadJob) {
# If Start-ThreadJob is available, add an approach for it.
$approachImpl['Start-ThreadJob'] = {
param([array] $batch)
$batch |
ForEach-Object {
Start-ThreadJob -ThrottleLimit $BatchSize {
Invoke-Expression ($using:exe + ' ' + ($using:exeArgList -f $args[0]))
} -ArgumentList $_
} |
Receive-Job -Wait -AutoRemoveJob | Out-Null
}
}
# ForEach-Object -Parallel job
if (-not $noForEachParallel) {
$approachImpl['ForEach-Object -Parallel'] = {
param([array] $batch)
$batch | ForEach-Object -ThrottleLimit $BatchSize -Parallel {
Invoke-Expression ($using:exe + ' ' + ($using:exeArgList -f $_))
}
}
}
# direct execution of an external program
$approachImpl['Start-Process'] = {
param([array] $batch)
$batch |
ForEach-Object {
Start-Process -NoNewWindow -PassThru $exe -ArgumentList ($exeArgList -f $_)
} |
Wait-Process
}
# Partition the array of all indices into subarrays (batches)
$batches = @(
0..([math]::Ceiling($zipFiles.Count / $batchSize) - 1) | ForEach-Object {
, $zipFiles[($_ * $batchSize)..($_ * $batchSize + $batchSize - 1)]
}
)
$tsTotals = foreach ($appr in $approaches) {
$i = 0
$tsTotal = [timespan] 0
$batches | ForEach-Object {
Write-Verbose "$batchSize-element '$appr' batch"
$ts = Measure-Command { & $approachImpl[$appr] $_ | Out-Null }
$tsTotal += $ts
if (++$i -eq $batches.Count) {
# last batch processed.
if ($batches.Count -gt 1) {
Write-Verbose ("'$appr' processing $JobCount items finished in " +
"$($tsTotal.TotalSeconds.ToString('N2')) secs.")
}
$tsTotal # output the overall timing for this approach
}
}
}
# Output a result object with the overall timings.
$oht = [ordered] @{}
$oht['JobCount'] = $JobCount
$oht['BatchSize'] = $BatchSize
$oht['BatchCount'] = $batches.Count
$i = 0
foreach ($appr in $approaches) {
$oht[($appr + ' (secs.)')] = $tsTotals[$i++].TotalSeconds.ToString('N2')
}
[pscustomobject] $oht
}
Az alábbi példa Measure-Parallel
segítségével 20 feladatot futtat párhuzamosan, egyszerre 5 feladatot, minden elérhető módszert kihasználva.
Measure-Parallel -Approach All -BatchSize 5 -JobCount 20 -Verbose
A következő kimenet egy PowerShell 7.5.1-et futtató Windows-számítógépről származik. Az időzítés számos tényezőtől függően változhat, de az arányoknak viszonylagos teljesítményt kell nyújtaniuk.
VERBOSE: 5-element 'Start-Job' batch
VERBOSE: 5-element 'Start-Job' batch
VERBOSE: 5-element 'Start-Job' batch
VERBOSE: 5-element 'Start-Job' batch
VERBOSE: 'Start-Job' processing 20 items finished in 7.58 secs.
VERBOSE: 5-element 'Start-ThreadJob' batch
VERBOSE: 5-element 'Start-ThreadJob' batch
VERBOSE: 5-element 'Start-ThreadJob' batch
VERBOSE: 5-element 'Start-ThreadJob' batch
VERBOSE: 'Start-ThreadJob' processing 20 items finished in 2.37 secs.
VERBOSE: 5-element 'Start-Process' batch
VERBOSE: 5-element 'Start-Process' batch
VERBOSE: 5-element 'Start-Process' batch
VERBOSE: 5-element 'Start-Process' batch
VERBOSE: 'Start-Process' processing 20 items finished in 0.26 secs.
VERBOSE: 5-element 'ForEach-Object -Parallel' batch
VERBOSE: 5-element 'ForEach-Object -Parallel' batch
VERBOSE: 5-element 'ForEach-Object -Parallel' batch
VERBOSE: 5-element 'ForEach-Object -Parallel' batch
VERBOSE: 'ForEach-Object -Parallel' processing 20 items finished in 0.79 secs.
JobCount : 20
BatchSize : 5
BatchCount : 4
Start-Job (secs.) : 7.58
Start-ThreadJob (secs.) : 2.37
Start-Process (secs.) : 0.26
ForEach-Object -Parallel (secs.) : 0.79
Összegzés
- A
Start-Process
megközelítés azért működik a legjobban, mert nem jár a feladatkezelés többletterhelésével. Azonban, mint korábban említettük, ez a megközelítés alapvető korlátozásokkal rendelkezik. - A
ForEach-Object -Parallel
legkisebb többletterhelést adja hozzá, majd aStart-ThreadJob
. -
Start-Job
a legtöbb többletterhelést az egyes feladatokhoz létrehozott rejtett PowerShell-példányok okozták.
Köszönetnyilvánítás
Az információk nagy része ez a cikk Santiago Squarzon és mklement0 válaszán alapul ebben a Stack Overflow bejegyzésben.
A Santiago Squarzon által létrehozott PSParallelPipeline modul is érdekelheti.