Az előző fejezetben látott „job”-ok, bár nagyon kényelmesen használhatók a kapcsolódó cmdletek segítségével, mégis rendelkeznek néhány hátrányos tulajdonsággal:
Létrehozásuk viszonylag lassú, így csak akkor éri meg a használatuk, ha egy-egy job tényleg hosszú ideig fut. Ha sok rövid job-ot akarunk futtatni, akkor a „hasznos teher” feldolgozására fordított idő kevesebb lehet, mint maguknak a joboknak a létrehozása.
A job kimenete szerializáláson esik keresztül, azaz a bonyolult objektumok tulajdonságai egy rekurzív eljárással a háttérben XML dokumentummá alakulnak át, melyek a globális scope-ban visszaalakulnak objektummá (deszerializálás). Miután az XML nem egy nagyon tömör adattárolási módszer, ezért ha sok objektum van a kimenetben, akkor ez a folyamat is elég időigényes lehet.
Jó hír, hogy a [powershell] osztály felhasználásával a háttérben futtatás egy alternatív módját is használhatjuk. Rögtön mérjük is le a hagyományos job-ok és az alternatív módszer sebességének különbségét!
Az első szkriptben létrehozok 10 hagyományos job-ot, ami valójában egy Get-Process cmdletet futtat. Most, miután a $parameters a 4. sorban az éppen futó PowerShell folyamatra szűr, ezért a háttérben futó folyamat csak egy processz objektumot ad vissza.
$script = New-Object -TypeName object[] -ArgumentList 10
$output = New-Object -TypeName object[] -ArgumentList 10
$job = New-Object -TypeName object[] -ArgumentList 10
$parameters = "-id $pid"
#$parameters = ""
$start = Get-Date
for($i = 0; $i -lt 10; $i++){
Write-Host "Creating job $i"
$script[$i] = [scriptblock]::create("Get-Process $parameters; ""Thread $i""")
$job[$i] = Start-Job -ScriptBlock $script[$i]
}
$createend = Get-Date
$creationtime = ($createend - $start).Totalseconds
write-host "Létrehozás tartott: $creationtime mp"
do {
$stillrunning = $false
Start-Sleep -Milliseconds 100
for($i = 0; $i -lt 10; $i++){
if($job[$i]){
if($job[$i].state -eq "Completed"){
$output[$i] = Receive-Job -Job $job[$i]
Remove-Job -Job $job[$i]
$job[$i] = $null
}
else{
$stillrunning = $true
}
}
}
} while ($stillrunning)
$executiontime = ((Get-Date) - $createend).Totalseconds
write-host "Futtatás tartott ($parameters): $executiontime mp"
Nézzük futtatásának eredményét:
PS C:\> . C:\PSKönyv\Realjobs.ps1
Creating job 0
Creating job 1
Creating job 2
Creating job 3
Creating job 4
Creating job 5
Creating job 6
Creating job 7
Creating job 8
Creating job 9
Létrehozás tartott: 3.0151724 mp
Futtatás tartott (-id 6292): 2.5181441 mp
Látható, hogy a 10 job létrehozása kb. 3 másodpercig tartott és futtatásuk csak 1-1-processz objektummal kb. 2.5 másodpercig.
Az $output tömbbe gyűjtött processz objektumok típusa a szerializálás-deszerializálás után így néz ki:
PS C:\> $output[0][0].pstypenames[0]
Deserialized.System.Diagnostics.Process
Ha kiveszem a kommentet az 5. sorból és berakom a 4. sor elejére, akkor az összes processz kilistázásra kerül. A futási idők ezzel így alakulnak:
PS C:\> C:\PSKönyv\Realjobs.ps1
Creating job 0
Creating job 1
Creating job 2
Creating job 3
Creating job 4
Creating job 5
Creating job 6
Creating job 7
Creating job 8
Creating job 9
Létrehozás tartott: 3.3111893 mp
Futtatás tartott (): 9.5175444 mp
Látható, hogy így a job-ok futtatása, a sokkal nagyobb kimenetnek köszönhetően sokkal több, kb. négyszer annyi ideig tartott, ez a szerializálás-deszerializálás számlájára írható.
Nézzük ugyanezt a szkriptet az alternatív módon:
$script = New-Object -TypeName object[] -ArgumentList 10
$thread = New-Object -TypeName object[] -ArgumentList 10
$output = New-Object -TypeName object[] -ArgumentList 10
$job = New-Object -TypeName object[] -ArgumentList 10
$parameters = "-id $pid"
#$parameters = ""
$start = Get-Date
for($i = 0; $i -lt 10; $i++){
Write-Host "Creating job $i"
$script[$i] = [scriptblock]::create("Get-Process $parameters; ""Thread $i""")
$thread[$i] = [powershell]::Create()
[void] $thread[$i].AddScript($script[$i])
$job[$i] = $thread[$i].BeginInvoke()
}
$createend = Get-Date
$creationtime = ($createend - $start).Totalseconds
write-host "Létrehozás tartott: $creationtime mp"
do {
$stillrunning = $false
Start-Sleep -Milliseconds 100
for($i = 0; $i -lt 10; $i++){
if($job[$i]){
if($job[$i].IsCompleted){
$output[$i] = $thread[$i].EndInvoke($job[$i])
$thread[$i].Runspace.Close()
$thread[$i].Dispose()
$job[$i] = $null
}
else{
$stillrunning = $true
}
}
}
} while ($stillrunning)
$executiontime = ((Get-Date) - $createend).Totalseconds
write-host "Futtatás tartott ($parameters): $executiontime mp"
És a futtatási eredmények, először egy processzel:
PS C:\> . C:\PSKönyv\Alternatívjobs.ps1
Creating job 0
Creating job 1
Creating job 2
Creating job 3
Creating job 4
Creating job 5
Creating job 6
Creating job 7
Creating job 8
Creating job 9
Létrehozás tartott: 0.1460084 mp
Futtatás tartott (-id 8680): 0.4150237 mp
Látható, hogy jóval gyorsabb lett mind a job-ok elkészítése és a futtatásuk. Ráadásul a kapott eredmény natív processz objektum, nem pedig egy szerializáláson átesett módosított objektum:
PS C:\> $output[0][0].pstypenames[0]
System.Diagnostics.Process
És nézzük az összes processzel:
Creating job 0
Creating job 1
Creating job 2
Creating job 3
Creating job 4
Creating job 5
Creating job 6
Creating job 7
Creating job 8
Creating job 9
Létrehozás tartott: 0.7194031 mp
Futtatás tartott (): 1.9968035 mp
Itt még nagyobb a nyereség, hiszen a nagyobb kimenet hatékonyabb átadásán többet spóroltunk.