Op een gegeven moment zal de meerderheid van de mensen tegen een probleem aanlopen waarbij een eenvoudig PowerShell-script gewoon te langzaam is om het op te lossen. Dit kan het verzamelen van gegevens zijn van veel computers in uw netwerk of misschien het tegelijkertijd aanmaken van een groot aantal nieuwe gebruikers in Active Directory. Dit zijn beide uitstekende voorbeelden van situaties waarin het gebruik van meer verwerkingskracht ervoor zou zorgen dat uw code sneller wordt uitgevoerd. Laten we bespreken hoe dit op te lossen met PowerShell-multithreading!
De standaard PowerShell-sessie is single-threaded. Het voert één opdracht uit en wanneer het klaar is, gaat het naar de volgende opdracht. Dit is handig omdat het alles herhaalbaar houdt en niet veel middelen gebruikt. Maar wat als de uitgevoerde acties niet van elkaar afhankelijk zijn en je de CPU-resources kunt missen? In dat geval is het tijd om na te denken over multithreading.
In dit artikel leer je hoe je verschillende PowerShell-multithreading-technieken kunt begrijpen en gebruiken om meerdere gegevensstromen tegelijkertijd te verwerken, maar beheerd via dezelfde console.
Begrip van PowerShell Multithreading
Multithreading is een manier om meer dan één opdracht tegelijk uit te voeren. Waar PowerShell normaal gesproken een enkele thread gebruikt, zijn er veel manieren om er meer dan één te gebruiken om uw code te paralleliseren.
Het belangrijkste voordeel van multithreading is het verminderen van de looptijd van de code. Deze tijdsvermindering gaat echter gepaard met een hogere verwerkingskrachtvereiste. Bij multithreading worden veel acties tegelijkertijd uitgevoerd, waardoor er meer systeembronnen nodig zijn.
Bijvoorbeeld, wat als je één nieuwe gebruiker wilt maken in Active Directory? In dit voorbeeld is er niets om meerdraads te maken omdat slechts één opdracht wordt uitgevoerd. Dit verandert allemaal wanneer je 1000 nieuwe gebruikers wilt maken.
Zonder meerdraads te zijn, zou je het New-ADUser
commando 1000 keer uitvoeren om alle gebruikers aan te maken. Misschien duurt het drie seconden om een nieuwe gebruiker aan te maken. Om alle 1000 gebruikers aan te maken, zou het net geen uur duren. In plaats van één thread te gebruiken voor 1000 commando’s, zou je in plaats daarvan 100 threads kunnen gebruiken die elk tien commando’s uitvoeren. Nu, in plaats van ongeveer 50 minuten te duren, ben je binnen een minuut klaar!
Merk op dat je geen perfecte schaalbaarheid zult zien. Het opstarten en afbreken van items in de code zal wat tijd kosten. Bij het gebruik van een enkele thread, moet PowerShell de code uitvoeren en dan is het klaar. Met meerdere threads zal de oorspronkelijke thread die wordt gebruikt om je console uit te voeren, worden gebruikt om de andere threads te beheren. Op een bepaald punt zal die oorspronkelijke thread maximaal worden belast om alle andere threads onder controle te houden.
Vereisten voor PowerShell Multithreading
Je gaat in dit artikel hands-on leren hoe PowerShell multithreading werkt. Als je wilt volgen, zijn hier een paar dingen die je nodig hebt en enkele details over de omgeving die wordt gebruikt.
- Windows PowerShell-versie 3 of hoger – Tenzij expliciet vermeld, zal alle gedemonstreerde code werken in Windows PowerShell-versie 3 of hoger. Voor de voorbeelden zal Windows PowerShell-versie 5.1 worden gebruikt.
- Spare CPU en geheugen – Je hebt minimaal wat extra CPU en geheugen nodig om te paralleliseren met PowerShell. Als dit niet beschikbaar is, zie je mogelijk geen prestatievoordeel.
Prioriteit #1: Repareer je code!
Voordat je je scripts versnelt met PowerShell-multithreading, zijn er enkele voorbereidende stappen die je wilt voltooien. Het eerste is het optimaliseren van je code.
Hoewel je meer middelen kunt toewijzen aan je code om het sneller te laten draaien, brengt multithreading veel extra complexiteit met zich mee. Als er manieren zijn om je code te versnellen vóór multithreading, moeten die eerst worden uitgevoerd.
Identificeer knelpunten
Een van de eerste stappen bij het paralleliseren van je code is om te achterhalen wat het vertraagt. De code kan traag zijn door slechte logica of extra lussen waar je wijzigingen kunt aanbrengen om snellere uitvoering mogelijk te maken voordat multithreading wordt toegepast.
Een voorbeeld van een veelvoorkomende manier om je code te versnellen, is het verschuiven van je filtering naar links. Als je bezig bent met een hoop gegevens, moet elke filtering die je wilt toepassen om de hoeveelheid gegevens te verminderen, zo vroeg mogelijk worden gedaan. Hieronder staat een voorbeeld van wat code om de hoeveelheid CPU te krijgen die wordt gebruikt door het svchost-proces.
De onderstaande voorbeeld leest alle lopende processen en filtert vervolgens een enkel proces uit (svchost). Het selecteert vervolgens de CPU-eigenschap en zorgt ervoor dat de waarde niet leeg is.
Vergelijk de bovenstaande code met het onderstaande voorbeeld. Hier is een ander voorbeeld van code met dezelfde uitvoer, maar anders georganiseerd. Let op dat de onderstaande code eenvoudiger is en alle mogelijke logica naar links van het pijlsymbool verschuift. Dit voorkomt dat Get-Process
processen retourneert waarin je niet geïnteresseerd bent.
Hieronder staat het tijdsverschil voor het uitvoeren van de twee regels hierboven. Hoewel het verschil van 117 ms misschien niet merkbaar is als je deze code slechts één keer uitvoert, zal het zich opstapelen bij duizenden uitvoeringen.

Gebruik van Thread-Safe Code
Zorg er vervolgens voor dat je code “thread-safe” is. De term “thread-safe” verwijst naar het feit dat als één thread code uitvoert, een andere thread dezelfde code tegelijkertijd kan uitvoeren zonder conflicten te veroorzaken.
Bijvoorbeeld, schrijven naar hetzelfde bestand in twee verschillende threads is niet thread-safe, omdat het niet weet wat eerst aan het bestand moet worden toegevoegd. Daarentegen is het lezen van een bestand door twee threads wel thread-safe, omdat het bestand niet wordt gewijzigd. Beide threads krijgen dezelfde uitvoer.
Het probleem met PowerShell multithreading-code die niet thread-safe is, is dat je inconsistente resultaten kunt krijgen. Soms werkt het prima omdat de threads toevallig op het juiste moment draaien en geen conflict veroorzaken. Andere keren heb je wel een conflict, wat het oplossen van het probleem bemoeilijkt vanwege inconsistente fouten.
Als je slechts twee of drie taken tegelijk uitvoert, kunnen ze toevallig allemaal zo zijn gesynchroniseerd dat ze allemaal op verschillende tijdstippen naar het bestand schrijven. Dan, wanneer je de code opschroeft naar 20 of 30 taken, wordt de kans kleiner dat ten minste twee van de taken proberen tegelijkertijd te schrijven.
Parallelle Uitvoering met PSJobs
Een van de makkelijkste manieren om een script te multithreaden is met PSJobs. PSJobs hebben cmdlets ingebouwd in de Microsoft.PowerShell.Core module. De Microsoft.PowerShell.Core module is inbegrepen in alle versies van PowerShell sinds versie 3. Commando’s in deze module laten je code op de achtergrond draaien terwijl je verschillende code op de voorgrond blijft draaien. Hieronder vind je een lijst van alle beschikbare commando’s.

Je Taken Bijhouden
Alle PSJobs zijn in een van de elf staten. Deze staten zijn hoe PowerShell de taken beheert.
Hieronder vind je een lijst van de meest voorkomende staten waarin een taak kan zijn.
- Voltooid – De taak is voltooid en de uitvoer kan worden opgehaald of de taak kan worden verwijderd.
- Bezig – De taak is momenteel bezig en kan niet worden verwijderd zonder de taak krachtig te stoppen. Ook kan de uitvoer nog niet worden opgehaald.
- Geblokkeerd – De taak is nog steeds bezig, maar de host wordt gevraagd om informatie voordat het kan doorgaan.
- Mislukt – Er is een fatale fout opgetreden tijdens de uitvoering van de taak.
Om de status van een gestarte taak te krijgen, gebruikt u het Get-Job
commando. Dit commando haalt alle attributen van uw taken op.
Hieronder staat de uitvoer voor een taak waarbij u kunt zien dat de status Voltooid is. Het onderstaande voorbeeld voert de code Start-Sleep 5
uit binnen een taak met behulp van het Start-Job
commando. De status van die taak wordt vervolgens opgehaald met behulp van het Get-Job
commando.

Wanneer de taakstatus Voltooid terugkeert, betekent dit dat de code in het scriptblok is uitgevoerd en klaar is met uitvoeren. U kunt ook zien dat de HasMoreData
eigenschap False
is. Dit betekent dat er geen uitvoer meer was om te leveren nadat de taak was voltooid.
Hieronder staat een voorbeeld van enkele andere statussen die worden gebruikt voor het beschrijven van taken. U kunt zien aan de Command
kolom wat sommige van deze taken mogelijk heeft verhinderd om te voltooien, zoals proberen te slapen voor abc
seconden resulteerde in een mislukte taak.

Het maken van nieuwe taken
Zoals u hierboven hebt gezien, stelt het Start-Job
commando u in staat om een nieuwe taak te maken die begint met het uitvoeren van code in de taak. Wanneer u een taak maakt, geeft u een scriptblok op dat wordt gebruikt voor de taak. De PSJob maakt vervolgens een taak met een uniek ID-nummer aan en begint de taak uit te voeren.
Het belangrijkste voordeel hier is dat het minder tijd kost om het Start-Job
-commando uit te voeren dan om het scriptblok dat we gebruiken uit te voeren. Je kunt in de onderstaande afbeelding zien dat in plaats van het commando vijf seconden te laten duren om te voltooien, het slechts 0,15 seconden duurde om de taak te starten.

De reden dat het dezelfde code kon uitvoeren in een fractie van de tijd was omdat het als een PSJob op de achtergrond werd uitgevoerd. Het duurde 0,15 seconden om de code in te stellen en te starten op de achtergrond in plaats van deze op de voorgrond uit te voeren en daadwerkelijk vijf seconden te slapen.
Opvragen van taakuitvoer
Soms retourneert de code binnen de taak output. U kunt de output van die code ophalen met het Receive-Job
-commando. Het Receive-Job
-commando accepteert een PSJob als invoer en schrijft vervolgens de output van de taak naar de console. Alles wat door de taak is uitvoerd tijdens het uitvoeren, is opgeslagen, zodat wanneer de taak wordt opgehaald, alle output van dat moment wordt weergegeven.
Een voorbeeld hiervan is het uitvoeren van de onderstaande code. Dit zal een taak maken en starten die Hallo Wereld naar de output zal schrijven. Vervolgens haalt het de output van de taak op en geeft het deze weer op de console.

Geplande taken maken
Een andere manier waarop je kunt interageren met PSJobs is via een geplande taak. Geplande taken lijken op een Windows geplande taak die kan worden geconfigureerd met Task Scheduler. Geplande taken bieden een manier om complexe PowerShell-scriptblokken eenvoudig in een geplande taak te plannen. Met een geplande taak kun je een PSJob op de achtergrond uitvoeren op basis van triggers.
Job Triggers
Jobtriggers kunnen dingen zijn zoals een specifieke tijd, wanneer een gebruiker zich aanmeldt, wanneer het systeem opstart en vele andere gebeurtenissen. Je kunt ook instellen dat de triggers zich herhalen op intervallen. Al deze triggers worden gedefinieerd met het New-JobTrigger
-commando. Dit commando wordt gebruikt om een trigger te specificeren die de geplande taak zal starten. Een geplande taak zonder trigger moet handmatig worden gestart, maar elke taak kan meerdere triggers hebben.
Naast het hebben van een trigger zou je nog steeds een scriptblok hebben, net zoals bij een normale PSJob. Zodra je zowel de trigger als het scriptblok hebt, gebruik je het Register-ScheduledJob
-commando om de taak aan te maken, zoals wordt getoond in de volgende sectie. Dit commando wordt gebruikt om kenmerken van de geplande taak op te geven, zoals het scriptblok dat wordt uitgevoerd en triggers gemaakt met het New-JobTrigger
-commando.
Demo
Misschien heb je wat PowerShell-code nodig die elke keer wordt uitgevoerd wanneer iemand zich aanmeldt bij een computer. Je kunt hiervoor een geplande taak maken.
Om dit te doen, zou je eerst een trigger definiëren met behulp van New-JobTrigger
en de geplande taak definiëren zoals hieronder wordt getoond. Deze geplande taak zal telkens wanneer iemand inlogt een regel naar een logbestand schrijven.
Zodra je de bovenstaande commando’s uitvoert, krijg je een output die lijkt op het maken van een nieuwe taak, waarbij het taak-ID, de scriptblok en enkele andere attributen worden weergegeven zoals hieronder wordt getoond.

Na een paar inlogpogingen kun je aan de onderstaande schermafbeelding zien dat de pogingen zijn gelogd.

Profiterend van de parameter AsJob
Nog een manier om taken te gebruiken, is door de parameter AsJob
te gebruiken die is ingebouwd in veel PowerShell-commando’s. Aangezien er veel verschillende commando’s zijn, kun je ze allemaal vinden met Get-Command
zoals hieronder wordt getoond.
Een van de meest voorkomende commando’s is Invoke-Command
. Normaal gesproken, wanneer je dit commando uitvoert, begint het onmiddellijk met het uitvoeren van een commando. Hoewel sommige commando’s onmiddellijk terugkeren, waardoor je kunt doorgaan met waar je mee bezig was, wachten sommige tot het commando is voltooid.
Het gebruik van de parameter AsJob
doet precies wat het klinkt en voert het uitgevoerde commando uit als een taak in plaats van het synchroon uit te voeren in de console.
Hoewel de meeste keren AsJob
kan worden gebruikt met de lokale machine, heeft Invoke-Command
geen native optie om op de lokale machine uit te voeren. Er is een omweg door Localhost te gebruiken als de waarde voor de parameter ComputerName
. Hieronder staat een voorbeeld van deze omweg.
Om de parameter AsJob
in actie te tonen, gebruikt het onderstaande voorbeeld Invoke-Command
om vijf seconden te wachten en vervolgens dezelfde opdracht te herhalen met behulp van AsJob
om het verschil in uitvoeringstijden te laten zien.

Runspaces: Soort van zoals Jobs maar sneller!
Tot nu toe heb je geleerd over manieren om extra threads te gebruiken met PowerShell door alleen de ingebouwde commando’s te gebruiken. Een andere optie om je script multithreaded te maken, is het gebruik van een aparte runspace.
Runspaces zijn het afgesloten gebied waarbinnen de thread(s) die PowerShell uitvoeren, werken. Terwijl de runspace die wordt gebruikt met de PowerShell-console beperkt is tot een enkele thread, kun je extra runspaces gebruiken om extra threads te gebruiken.
Runspace vs PSJobs
Hoewel een runspace en een PSJob veel overeenkomsten hebben, zijn er enkele grote verschillen in prestaties. Het grootste verschil tussen runspaces en PSjobs is de tijd die nodig is om deze op te zetten en af te breken.
In het voorbeeld uit het vorige gedeelte duurde het ongeveer 150 ms om de PSjob te maken. Dit is ongeveer het beste geval, omdat het scriptblok voor de job helemaal geen code bevatte en er geen extra variabelen naar de job werden doorgegeven.
In tegenstelling tot de creatie van een PSJob wordt een runspace van tevoren gemaakt. Het grootste deel van de tijd die nodig is om een runspace-job op te starten, wordt afgehandeld voordat er enige code is toegevoegd.
Hieronder staat een voorbeeld van het uitvoeren van dezelfde opdracht die we voor de PSjob hebben gebruikt, maar nu in de runspace.

In tegenstelling hiermee is hieronder de code gebruikt voor de runspace-versie. Je kunt zien dat er veel meer code is om dezelfde taak uit te voeren. Maar het voordeel van de extra code scheelt bijna 3/4 van de tijd, waardoor het commando begint uit te voeren in 36 ms in plaats van 148 ms.

Runspaces uitvoeren: Een stapsgewijze handleiding
Het gebruik van runspace kan in het begin een ontmoedigende taak zijn, aangezien er geen PowerShell-commando-begeleiding meer is. U moet rechtstreeks omgaan met .NET-klassen. Laten we in dit gedeelte eens kijken naar wat er nodig is om een runspace te maken in PowerShell.
In deze handleiding maak je een aparte runspace vanuit je PowerShell-console en een aparte PowerShell-instantie. Vervolgens wijs je de nieuwe runspace toe aan de nieuwe PowerShell-instantie en voeg je code toe aan die instantie.
Maak de Runspace
Het eerste wat je moet doen, is je nieuwe runspace maken. Dit doe je met behulp van de klasse runspacefactory
. Sla dit op in een variabele, zoals hieronder wordt getoond, zodat het later kan worden geraadpleegd.
Nu de runspace is gemaakt, wijs je deze toe aan een PowerShell-instantie om PowerShell-code uit te voeren. Hiervoor gebruik je de klasse powershell
en vergelijkbaar met de runspace, moet je dit ook opslaan in een variabele, zoals hieronder wordt getoond.
Voeg vervolgens de runspace toe aan je PowerShell-instantie, open de runspace om code uit te voeren en voeg je scriptblock toe. Dit wordt hieronder getoond met een scriptblock om vijf seconden te slapen.
Voer de Runspace uit
Tot nu toe is het scriptblok nog niet uitgevoerd. Alles wat tot nu toe is gedaan, is het definiëren van alles voor de runspace. Om het scriptblok daadwerkelijk uit te voeren, heb je twee opties.
- Invoke() – De
Invoke()
-methode voert het scriptblok uit in de runspace, maar wacht met terugkeren naar de console totdat de runspace terugkeert. Dit is handig om te testen en ervoor te zorgen dat je code correct wordt uitgevoerd voordat je het loslaat. - BeginInvoke() – Gebruik de
BeginInvoke()
-methode als je daadwerkelijk prestatieverbetering wilt zien. Hiermee wordt het scriptblok gestart in de runspace en keer je onmiddellijk terug naar de console.
Bij gebruik van BeginInvoke()
moet je de uitvoer opslaan in een variabele, omdat dit nodig is om de status van het scriptblok in de runspace te controleren, zoals hieronder wordt getoond.
Zodra je de uitvoer van BeginInvoke()
hebt opgeslagen in een variabele, kun je die variabele controleren om de status van de taak te zien, zoals hieronder wordt weergegeven in de eigenschap IsCompleted
.

Nog een reden om de uitvoer in een variabele op te slaan, is omdat, in tegenstelling tot de Invoke()
-methode, BeginInvoke()
de uitvoer niet automatisch teruggeeft wanneer de code is voltooid. Om dit te doen, moet je de EndInvoke()
-methode gebruiken zodra deze is voltooid.
In dit voorbeeld zou er geen uitvoer zijn, maar om de invoke te beëindigen, zou je de onderstaande opdracht gebruiken.
Zodra alle taken die je in de runspace hebt gepland zijn voltooid, moet je altijd de runspace sluiten. Dit zal het geautomatiseerde opruimproces van PowerShell activeren om ongebruikte bronnen op te schonen. Hieronder staat het commando dat je hiervoor zou gebruiken.
Gebruik van Runspace-pools
Hoewel het gebruik van een runspace de prestaties verbetert, heeft het te maken met een belangrijke beperking van een enkele thread. Hier schitteren runspace-pools in hun gebruik van meerdere threads.
In het vorige gedeelte gebruikte je slechts twee runspaces. Je gebruikte er slechts één voor de PowerShell-console zelf en degene die je handmatig had aangemaakt. Runspace-pools stellen je in staat om meerdere runspaces te hebben die op de achtergrond worden beheerd met behulp van één variabele.
Hoewel dit gedrag met meerdere runspaces kan worden bereikt met meerdere runspace-objecten, maakt het gebruik van een runspace-pool het beheer veel eenvoudiger.
Runspace-pools verschillen van enkele runspaces in hoe ze worden opgezet. Een van de belangrijkste verschillen is dat je het maximale aantal threads definieert dat kan worden gebruikt voor de runspace-pool. Met een enkele runspace is dit beperkt tot één thread, maar met een pool specificeer je het maximale aantal threads waar de pool naar kan schalen.
Het aanbevolen aantal threads in een runspace-pool is afhankelijk van de hoeveelheid taken die worden uitgevoerd, en de machine waarop de code wordt uitgevoerd. Hoewel het verhogen van het maximale aantal threads in de meeste gevallen de snelheid niet negatief beïnvloedt, zie je mogelijk ook geen voordeel.
Demonstratie van Runspace-pool Snelheid
Om een voorbeeld te laten zien waarbij een runspace pool beter presteert dan een enkele runspace, wil je misschien tien nieuwe bestanden maken. Als je een enkele runspace zou gebruiken voor deze taak, zou je het eerste bestand maken, dan naar het tweede gaan, en dan naar het derde, en zo verder totdat alle tien zijn gemaakt. Het scriptblok voor dit voorbeeld zou er als volgt uit kunnen zien. Je zou dit scriptblok tien bestandsnamen doorgeven in een lus en ze zouden allemaal worden gemaakt.
In het onderstaande voorbeeld is een scriptblok gedefinieerd dat een kort script bevat dat een naam accepteert en een bestand met die naam maakt. Er wordt een runspace-pool gemaakt met een maximum van 5 threads.
Vervolgens doorloopt een lus tien keer en telkens wijst het het nummer van de iteratie toe aan $_
. Dus het zou 1 hebben bij de eerste iteratie, 2 bij de tweede en zo verder.
De lus maakt een PowerShell-object, wijst het scriptblok toe, en het argument voor het script en start het proces.
Tenslotte, aan het einde van de lus, zal het wachten tot alle taken in de wachtrij zijn voltooid.
Nu, in plaats van threads één voor één te maken, zal het er vijf tegelijk maken. Zonder runspace pools zou je vijf afzonderlijke runspaces en vijf afzonderlijke Powershell
-instanties moeten maken en beheren. Dit beheer wordt al snel een rommeltje.
In plaats daarvan kun je een runspace pool maken, een PowerShell-instantie gebruiken, hetzelfde codeblok en dezelfde lus gebruiken. Het verschil is dat de runspace automatisch zal opschalen om al die vijf threads helemaal alleen te gebruiken.
Het aanmaken van Runspace Pools
De creatie van een runspace-pool is erg vergelijkbaar met de runspace die in een eerdere sectie is gemaakt. Hieronder staat een voorbeeld van hoe het moet worden gedaan. De toevoeging van een scriptblok en het aanroepen van het proces is identiek aan een runspace. Zoals je hieronder kunt zien, wordt de runspace-pool gecreëerd met een maximum van vijf threads.
Vergelijking van Runspaces en Runspace-pools voor Snelheid
Om het verschil tussen een runspace en een runspace-pool te laten zien, maak een runspace aan en voer het Start-Sleep
-commando uit zoals eerder. Dit keer moet het echter 10 keer worden uitgevoerd. Zoals je kunt zien in de onderstaande code, wordt er een runspace gemaakt die 5 seconden zal slapen.
Merk op dat omdat je een enkele runspace gebruikt, je moet wachten tot deze is voltooid voordat een andere aanroep kan worden gestart. Daarom is er een slaap van 100 ms toegevoegd totdat de taak is voltooid. Hoewel dit kan worden verminderd, zul je afnemende meeropbrengsten zien omdat je meer tijd zult besteden aan het controleren of de taak is voltooid dan aan het wachten op het einde van de taak.
Uit het onderstaande voorbeeld kun je zien dat het ongeveer 51 seconden duurde om 10 sets van 5 seconden slaap te voltooien.

Als alternatief voor het gebruik van een enkele runspace, schakel over naar een runspace-pool. Hieronder staat de code die wordt uitgevoerd. Je kunt zien dat er een paar verschillen zijn tussen het gebruik van de twee in de onderstaande code bij het gebruik van een runspace-pool.
Zoals je hieronder kunt zien, is dit voltooid in iets meer dan 10 seconden, wat aanzienlijk verbeterd is ten opzichte van de 51 seconden voor de enkele runspace.

Hieronder staat een samenvatting van het verschil tussen een runspace en een runspace-pool in deze voorbeelden.
Property | Runspace | Runspace Pool |
---|---|---|
Wait Delay | Waiting for each job to finish before continuing to the next. | Starting all of the jobs and then waiting until they have all finished. |
Amount of Threads | One | Five |
Runtime | 50.8 Seconds | 10.1 Seconds |
Het inleiden van Runspaces met PoshRSJob
A frequent occurrence when programming is that you will do what is more comfortable and accept the small loss in performance. This could be because it makes the code easier to write or easier to read, or it could just be your preference.
Hetzelfde gebeurt met PowerShell waar sommige mensen PSJobs zullen gebruiken in plaats van runspaces vanwege het gebruiksgemak. Er zijn een paar dingen die kunnen worden gedaan om het verschil te splitsen en betere prestaties te behalen zonder het te veel moeilijker te maken om te gebruiken.
Er is een veelgebruikte module genaamd PoshRSJob die modules bevat die passen bij de stijl van normale PSJobs maar met het toegevoegde voordeel van het gebruik van runspaces. In plaats van alle code te moeten specificeren om de runspace en het PowerShell-object te maken, behandelt de PoshRSJob-module al het werk wanneer u de commando’s uitvoert.
Om de module te installeren, voert u het onderstaande commando uit in een administratieve PowerShell-sessie.
Zodra de module is geïnstalleerd, kunt u zien dat de commando’s hetzelfde zijn als de PSJob-commando’s met een RS-voorvoegsel. In plaats van Start-Job
is het Start-RSJob
. In plaats van Get-Job
is het Get-RSJob
.
Hieronder staat een voorbeeld van hoe u hetzelfde commando kunt uitvoeren in een PSJob en vervolgens in een RSJob. Zoals u kunt zien, hebben ze zeer vergelijkbare syntaxis en uitvoer, maar ze zijn niet helemaal identiek.

Hieronder staat wat code die kan worden gebruikt om het verschil in snelheid tussen een PSJob en een RSJob te vergelijken.
Zoals u hieronder kunt zien, is er een groot snelheidsverschil omdat de RSJobs nog steeds runspaces onder de motorkap gebruiken.

Foreach-Object -Parallel
De PowerShell-gemeenschap wil al geruime tijd een eenvoudigere en ingebouwde manier om snel een proces met multithreading uit te voeren. De parallelle schakelaar is het resultaat daarvan.
Op het moment van schrijven is PowerShell 7 nog steeds in de previewfase, maar ze hebben een Parallel
-parameter toegevoegd aan de Foreach-Object
-opdracht. Dit proces gebruikt runspaces om de code te paralleliseren en gebruikt het scriptblok dat wordt gebruikt voor de Foreach-Object
als het scriptblok voor de runspace.
Hoewel de details nog worden uitgewerkt, kan dit een eenvoudigere manier zijn om runspaces in de toekomst te gebruiken. Zoals je hieronder kunt zien, kun je snel door veel reeksen van slaaptijden lussen.

Uitdagingen bij multithreading
Hoewel multithreading tot nu toe geweldig klonk, is dat niet helemaal het geval. Er zijn veel uitdagingen verbonden aan het toepassen van multithreading op code.
Het gebruik van variabelen
Een van de grootste en meest voor de hand liggende uitdagingen bij multithreading is dat je geen variabelen kunt delen zonder ze als argumenten door te geven. Er is één uitzondering, namelijk een gesynchroniseerde hashtable, maar dat is voer voor een andere dag.
Zowel PSJobs als runspaces werken zonder toegang tot bestaande variabelen, en er is geen manier om te communiceren met variabelen die in verschillende runspaces worden gebruikt vanuit je console.
Dit vormt een grote uitdaging voor het dynamisch doorgeven van informatie aan deze taken. Het antwoord verschilt afhankelijk van het type multithreading dat je gebruikt.
Voor Start-Job
en Start-RSJob
van de PoshRSJob-module, kunt u de ArgumentList
-parameter gebruiken om een lijst van objecten te verstrekken die als parameters aan het scriptblock worden doorgegeven in de volgorde waarin u ze vermeldt. Hieronder staan voorbeelden van de gebruikte commando’s voor PSJobs en RSJobs.
PSJob:
RSJob:
Natuurlijke runspaces bieden niet hetzelfde gemak. In plaats daarvan moet u de AddArgument()
-methode gebruiken op het PowerShell-object. Hieronder staat een voorbeeld van hoe het eruit zou zien voor elk.
Runspace:
Hoewel runspace-pools hetzelfde werken, is hieronder een voorbeeld van hoe u een argument aan een runspace-pool kunt toevoegen.
Logging
Meerdradigheid introduceert ook uitdagingen op het gebied van logging. Aangezien elke thread onafhankelijk van elkaar werkt, kunnen ze niet allemaal naar dezelfde plaats loggen. Als je bijvoorbeeld zou proberen om te loggen naar een bestand met meerdere threads, konden andere threads niet loggen terwijl één thread naar het bestand schreef. Dit kan uw code vertragen of ervoor zorgen dat deze volledig mislukt.
Als voorbeeld staat hieronder wat code om 100 keer te proberen te loggen naar een enkel bestand met behulp van 5 threads in een runspace-pool.
U zult geen fouten zien in de uitvoer, maar als u naar de grootte van het tekstbestand kijkt, kunt u hieronder zien dat niet alle 100 taken correct zijn voltooid.

Enkele manieren om dit te omzeilen zijn door naar aparte bestanden te loggen. Hiermee wordt het probleem van het vergrendelen van het bestand verwijderd, maar dan heeft u wel veel logbestanden waar u doorheen zou moeten gaan om alles wat er is gebeurd te achterhalen.
Een andere optie is dat u toestaat dat de timing van sommige van de output niet klopt en dat u alleen logt wat een taak heeft gedaan nadat deze is voltooid. Dit stelt u in staat om alles geserialiseerd te hebben via uw oorspronkelijke sessie, maar u verliest enkele details omdat u niet noodzakelijkerwijs weet in welke volgorde alles is gebeurd.
Samenvatting
Hoewel multithreading enorme prestatiewinsten kan opleveren, kan het ook hoofdpijn veroorzaken. Hoewel sommige workloads er enorm van zullen profiteren, geldt dit mogelijk helemaal niet voor andere. Er zijn veel voor- en nadelen verbonden aan het gebruik van multithreading, maar als het correct wordt gebruikt, kunt u de looptijd van uw code drastisch verminderen.
Verder lezen
Source:
https://adamtheautomator.com/powershell-multithreading/