PS Runspace
Wenn ich eine PowerShell starte, dann habe ich auch immer eine "Laufzeitumgebung", in der ich mich bewege. Wir wissen alle, dass wir auch weitere PowerShell-Fenster öffnen können oder mit Start-Job oder Start-Thread auch Befehle in den Hintergrund senden. Ich kann aber auch parallel weitere Runspaces instanzieren und Code dort parallel und im Hintergrund ausführen lassen.
Anstatt nun viel Text und Beispielcode zu lesen, können Sie sich das kurze Video von Adam Driscol anschauen, welches sehr gut die Nutzung von PowerShell Runspaces erklärt
Adan Driscol: Advanced PowerShell -
Runspaces
https://www.youtube.com/watch?app=desktop&v=WvxUuru_vhk
Mit PowerShell Core gibt es mit Start-ThreadJob auch eine neue Option zum Start von Prozessen im Hintergrund.
Eine ganz einfache Variante zeigt, was damit möglich ist und was nicht. Hier ein Code, der einfach ein "Get-Recipient" ausführen soll. Er wurde in einer Exchange PowerShell gestartet.
$PowerShell = [powershell]::Create() [void]$PowerShell.AddScript({ Get-Recipient -resultsize 3 }) $PowerShell.Invoke()
Der "$powershell.invoke()" startet den Code und wartet, bis er fertig ist. Das geht hier schnell und folgende Ausgaben kann ich generieren:
[PS] C:\>$PowerShell Commands : System.Management.Automation.PSCommand Streams : System.Management.Automation.PSDataStreams InstanceId : 5966f660-b993-4176-b5cd-a73d5a6b3ec7 InvocationStateInfo : System.Management.Automation.PSInvocationStateInfo IsNested : False HadErrors : True Runspace : System.Management.Automation.Runspaces.LocalRunspace RunspacePool : IsRunspaceOwner : True HistoryString :
Hier sehen Sie die verschiedenen Properties. Da sind "Commands" und "Streams" interessant:
[PS] C:\>$PowerShell.Commands | fl * Commands : { Get-recipient -resultsize 3 } [PS] C:\>$PowerShell.Streams Error : {The term 'Get-recipient' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.} Progress : {parent = -1 id = 0 act = Preparing modules for first use. stat = cur = pct = -1 sec = -1 type = Completed} Verbose : {} Debug : {} Warning : {} Information : {}
Im Property "Commands" stehen die Befehle, die ich übergeben hatte und auch immer wieder per "invoke" aufrufen könnte. Das Skript ist aber nicht gelaufen, weswegen hier im ERROR-Stream der Fehlercode steht. Damit sehe ich aber auch, dass der Runspace eine eigene Shell ist und nicht die Module und Einstellungen der aufrufenden Shell vererbt bekommt. Die Exchange Commandlets funktionieren hier einfach nicht.
Ich muss also in dem Runspace ggfls. meine erforderlich Umgebung noch einmal neu schaffen. Um die Befehle nun parallel auszuführen, muss ich aber "BeginInvoke()" nutzen. Damit wird das aufrufende Programm nicht angehalten.
# Code definieren $code = { 1..100 | % { $(get-date) start-sleep -seconds 1 } } #Objekt System.Management.Automation.PowerShell vorbereiten $newPowerShell = [PowerShell]::Create().AddScript($code) # Job starten $job = $newPowerShell.BeginInvoke() #Warten auf das Ende #While (-Not $job.IsCompleted) { # write-host " #} #Ergebnisse abholen. Wartet allerdings bis das Skript beendet ist. Notfalls endlos $newPowerShell.EndInvoke($job) # Aufraeumen $newPowerShell.Dispose()
Die so gestartet Jobs finden Sie nicht mit "Get-Job". Sie sind also gut beraten das Ergebnis des "BeginInvoke" irgendwo zu hinterlegen. Soweit ich gesehen habe, werden aber alle Ausgaben erst mit dem "EndInvoke()" zurück gegeben und auch erst dann, wenn der Code terminiert. Das Beispiel wird also erst nach 100 Sekunden beendet und dann die Ausgabe eingesammelt. Das ist zwar "schnell parallel" aber eine Weiterverarbeitung der Zwischenergebnisse ist so nicht möglich. Auch scheint die Laufzeitumgebung sich nicht zu vererben wenngleich es keinen weiteren "Powershell.exe"-Prozess im Taskmanager gibt. Es sieht wirklich nach einem eigenen Thread aus.
Weitere Links
- PowerShell Mutex
- Start-ThreadJob
- PowerShell Streams
- PS Job
- Power​Shell Class
https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.powershell?view=powershellsdk-1.1.0 - True Multithreading in PowerShell
PowerShell Add comments.
http://www.get-blog.com/?p=189 - Invoke-All
https://aka.ms/invokeall - Easily Multi thread Powershell commands
and scripts
https://blogs.technet.microsoft.com/santhse/invokeall/ - Using Background Runspaces Instead of
PSJobs For Better Performance
https://learn-powershell.net/2012/05/13/using-background-runspaces-instead-of-psjobs-for-better-performance/ - Multithread Your PowerShell Commands
Using Runspaces with PoshRSJob
https://mcpmag.com/articles/2015/08/06/multithread-your-commands.aspx - Beginning Use of PowerShell Runspaces:
Part1: https://devblogs.microsoft.com/scripting/beginning-use-of-powershell-runspaces-part-1/
Part2: https://devblogs.microsoft.com/scripting/beginning-use-of-powershell-runspaces-part-2/
Part3; https://devblogs.microsoft.com/scripting/beginning-use-of-powershell-runspaces-part-3/ - Weekend Scripter: A Look at the
PoshRSJob Module
https://devblogs.microsoft.com/scripting/weekend-scripter-a-look-at-the-poshrsjob-module/