- PowerShell Den tilbyr flere måter å iterere på (foreach, ForEach-Object, ForEach-metode, for, while, do-while, do-until) med forskjellige ytelses- og minneimplikasjoner.
- ForEach-Object -Parallel i PowerShell 7 tillater behandling av pipeline-elementer parallelt, kontroll av samtidighet gjennom ThrottleLimit og ofring av utdatarekkefølge.
- Start-Job, Start-ThreadJob, runspaces og eldre arbeidsflyter med foreach -Parallel gir andre modeller av parallellisme med forskjellige overbelastninger og isolasjonsnivåer.
- Å måle ytelse, unngå typiske foreach-feil og anvende gode flytkontroll- og feilhåndteringspraksiser er nøkkelen til å bygge kraftige og stabile pipelines.
Når du begynner å jobbe med PowerShell, støter du før eller siden på det samme problemet: du har mange objekter å behandle i en løkke og ytelsen synker kraftig. Utveksle postbokser, filer på en server, prosesser på tvers av flere maskiner, for eksempel administrere flere PC-er og servere...alt går gjennom rørledningen og den klassiske ForEach-ObjectMen selvfølgelig går den løkken element for element, sekvensielt, og det kommer et punkt hvor den kommer til kort.
Med ankomsten av PowerShell 7 endret landskapet seg betraktelig. Vi har nå parallell pipelining til rådighet med `ForEach-Object-Parallel` og andre samtidighetsmetoder som lar oss utnytte CPU-kjerner bedre og redusere ventetiden for nettverks- eller diskoperasjoner. Trikset er å forstå fullt ut hva hvert alternativ gjør, når det er passende å bruke det, og hvordan man begrenser samtidighet for ikke å forringe ytelsen i stedet for å forbedre den.
Looper i PowerShell: utgangspunktet
Før man dykker ned i parallelle pipelines, er det nyttig å forstå hva løkker er og hvordan ulike PowerShell-konstruksjoner oppfører seg. I hovedsak er en løkke en måte å gjenta én eller flere instruksjoner flere ganger , enten for et kjent antall iterasjoner eller mens en betingelse er oppfylt. I systemadministrasjon er dette vanlig: fra å gi tillatelser til alle brukere i en gruppe til å gå gjennom tusenvis av filer i en katalog eller behandle hendelser med Get-WinEvent.
PowerShell tilbyr flere klassiske kontrollstrukturer som for, foreach, while, do-while y do-until, i tillegg til cmdleter og metoder som også tillater iterasjon, for eksempel ForEach-Object eller metoden .ForEach() i samlinger. Alle disse danner grunnlaget som vi deretter kan bygge mer avansert logikk på, inkludert parallell utførelse.
foreach vs. ForEach-Object vs. ForEach-metoden
I PowerShell er det litt forvirring fordi vi har tre forskjellige måter å gjøre «foreach» på, og de oppfører seg ikke likt. Syntaktisk er de like, men internt endrer ting seg ganske mye, spesielt når det gjelder minnebruk og pipeline.
foreach nøkkelord (foreach utsagn)
Stikkordet foreach Det er den «klassiske» løkkekonstruksjonen til språket. Last inn Hele samlingen lagres i minnet, og hvert element gjennomgås sekvensielt.Det er praktisk og veldig raskt når du allerede har en array eller annen samling i minnet, og størrelsen er rimelig. Behandlingen følger disse trinnene: først bestemmer PowerShell hvor mange elementer det er, deretter tilordner den hvert element, ett etter ett, til en variabel (for eksempel $item) og kjører kodeblokken for hver iterasjon.
Denne tilnærmingen er ideell når man arbeider med veldefinerte minnesamlinger , for eksempel en liste over prosesser, et sett med ruter eller et tallområde. Den er imidlertid ikke designet for å lese direkte fra pipelinen, og hvis samlingen er veldig stor, kan den forbruke en betydelig mengde minne.
ForEach-Object-cmdlet i pipelinen
cmdleten ForEach-Object Det fungerer annerledes: det er designet for å behandle ett objekt om gangen når det ankommer gjennom pipelinenMed andre ord trenger den ikke å laste inn hele samlingen fra begynnelsen. Dette gjør den svært effektiv for scenarier der datakilden kan generere mange elementer (for eksempel Get-ChildItem (på et stort mappetre) eller når du ikke vil eller kan lagre hele samlingen i minnet, eller når du bruker eksterne verktøy som Git fra PowerShell.
Tilbake, Den har en liten overbelastning på grunn av selve pipeline-mekanismen og er vanligvis noe tregere enn nøkkelordet foreach når begge jobber på den samme samlingen i minnet. Det gir imidlertid poeng i skalerbarhet og minneeffektivitet, noe som i produksjonsmiljøer vanligvis er viktigere enn å presse inn noen få millisekunders hastighet per objekt.
ForEach()-metoden i samlinger
Siden PowerShell 5 eksponerer mange samlinger som arrayer og lister metoden .ForEach(), som tillater Bruk en scriptblock på hvert element på en veldig konsis måte. For eksempel, $array.ForEach({ ... })Denne metoden er objektorientert og er veldig nyttig når du vil kjede operasjoner og jobbe problemfritt med eksisterende samlinger.
Når det gjelder ytelse, metoden .ForEach() vanligvis mer effektiv enn cmdleten ForEach-Object Når alt allerede er i minnet, fordi du unngår deler av pipeline-mekanikken. Likevel deler den den samme grunnleggende begrensningen: den fungerer på samlinger som allerede er materialisert, så det er ikke det rette verktøyet for strømming av store datamengder.
Praktiske forskjeller mellom foreach og ForEach-Object
I den daglige praksisen oppstår spørsmål som: «Hvorfor dette script usa Get-Mailbox ... | ForEach-Object { ... } og en annen gjør det først $Mailboxes = Get-Mailbox ... og deretter en foreach ($Mailbox in $Mailboxes)Hovedforskjellen er at i det første tilfellet Alt går gjennom en rørledning, og behandler hver postkasse etter hvert som den ankommer, mens i den andre lagres hele samlingen i variabelen $Mailboxes og så reiser du rundt.
Valget er ikke bare et spørsmål om stil: hvis du håndterer tusenvis av elementer, kan minnebruk og feilhåndtering variere betydelig, og i dette tilfellet er pipelinen vanligvis mer forbruksvennlig, på bekostning av et lite ytelsestap per objekt.
Andre typer løkker: for, mens, do-while, do-until
I tillegg til foreach-økosystemet inkluderer PowerShell andre tradisjonelle løkker som fortsatt er svært nyttige. for tillater iterasjon over et tallområdeDet er lurt å øke eller redusere en indeks inntil en betingelse ikke lenger er oppfylt. Det er perfekt for å navigere i arrayer etter posisjon, hoppe over elementer eller få tilgang til dem i revers.
løkkene do-while y do-until De henretter kroppen minst én gang og så sjekker de tilstanden: i do-while den gjentas så lenge den er oppfylt, og i do-until Den gjentas til betingelsen er oppfylt. Disse er veldig nyttige når du trenger at blokken skal kjøres minst én gang, for eksempel når du ber om data fra brukeren eller tester en ressurs til den svarer riktig.
Løkke while vurder tilstanden helt fra starten avHvis betingelsen ikke er oppfylt, kommer den ikke inn i systemet. Det er den klassiske «så lenge dette forblir sant»-metoden som vi alle kjenner fra andre språk. I automatiseringsoppgaver kan den brukes til å overvåke en tjeneste, en numerisk verdi eller tilstanden til en ressurs inntil et mål er nådd.
Fin flytkontroll: pause, fortsett og returner
For å bedre kontrollere hva som skjer inne i løkkene, har vi tre viktige nøkkelord: break, continue y returnSelv om de noen ganger blir oversett, utgjør de en ganske stor forskjell i komplekse manus.
Instruksen break Det lar deg gå ut av løkken umiddelbart. nåværende. Det er veldig nyttig når du går gjennom en matrise eller en liste og leter etter noe spesifikt: så snart du finner det, gjør du det break og du sparer deg selv for unødvendig iterasjon.
Ordet continue hopp til neste løkkeiterasjonignorerer resten av instruksjonene i den gjeldende iterasjonen. Det brukes ofte til å "filtrere" elementer: hvis noe ikke oppfyller en betingelse, gjør du det continue og du bearbeider bare det som interesserer deg.
Endelig, return ikke bare bryter løkkenDen returnerer ikke bare en verdi fra en funksjon eller et skript, men den returnerer også en verdi fra selve skriptet. Det er som å si: «Jeg har det jeg lette etter, her er det, og jeg går.» Brukt klokt forenkler det skriptlogikken betraktelig.
ForEach-Object-Parallel i PowerShell 7
PowerShell 7 introduserte en veldig kraftig ny funksjon: parameteren -Parallel av cmdlet-en ForEach-ObjectDenne funksjonaliteten, som opprinnelig ble lagt til i beta 3 av versjon 7.0, tillater behandling av flere pipeline-elementer samtidig, uten å måtte ty til eksterne moduler eller kompleks kode med runspaces.
Ideen er enkel: i stedet for å behandle ett objekt etter det andre sekvensielt , kan cmdleten kjøre flere iterasjoner parallelt, der hver iterasjon kjører skriptblokken på et annet element i samlingen. På denne måten kan du redusere den totale utførelsestiden betydelig hvis du har uavhengige oppgaver (for eksempel flere HTTP-forespørsler, flere filkopier til forskjellige destinasjoner eller spørringer mot mange postbokser).
Oppførselen til ForEach-Object før og etter -Parallel
Uten parameteren -Parallel, ForEach-Object behandler elementene i rekkefølgeEn etter en, og vent på at hver blokk skal bli ferdig før du går videre til den neste. Hvis du gjør en enkel test med 10 objekter og en kort kunstig pause, vil du se at den totale tiden praktisk talt er summen av de 10 individuelle tidene.
Aktiverer -Parallel Den samme koden kan kjøres flere ganger raskere.Dette er fordi iterasjonene er fordelt på flere delprosesser. Et typisk eksempel: det som tar omtrent 10 sekunder i sekvensiell modus kan falle til mindre enn 3 sekunder parallelt, avhengig av antall samtidige oppgaver og typen arbeid du gjør innenfor blokken.
Utgangsordre og antall samtidige operasjoner
Det er én viktig detalj: når du bruker ForEach-Object -Parallel, The Rekkefølgen på resultatene er ikke lenger garantert.Elementene returneres etter hvert som oppgavene fullføres, ikke i sin opprinnelige rekkefølge. Hvis logikken din er avhengig av rekkefølge, må du endre rekkefølgen etterpå eller inkludere indeksinformasjon for å rekonstruere den.
Som standard kjører PowerShell 7 fem samtidige operasjonerHvis du trenger å justere dette, bruker du parameteren ThrottleLimitDette definerer det maksimale antallet elementer som kan behandles samtidig. Riktig konfigurering er nøkkelen til å finne den rette balansen mellom hastighet og ressursforbruk.
Når er det ikke tilrådelig å parallellisere?
Ikke alle problemer løses ved å implementere parallellisme vilkårlig. I noen scenarier, for eksempel store filoverføringer over et begrenset nettverk , kan det å kjøre for mange parallelle operasjoner mette båndbredden og paradoksalt nok gjøre alt tregere. Det samme gjelder API-er eller eksterne tjenester som har forespørselsgrenser.
I andre tilfeller, for eksempel operasjoner som er strengt avhengige av utførelsesrekkefølgen eller som endrer den samme delte ressursen , kan parallellisme introdusere kappløpsbetingelser eller inkonsekvenser. I disse sammenhengene er det vanligvis bedre å holde seg til en sekvensiell tilnærming eller bruke mer detaljert og kontrollert parallellisme.
Andre alternativer for parallellkjøring i PowerShell
Pluss ForEach-Object -Parallel, PowerShell tilbyr flere måter å jobbe parallelt påBåde i eldre versjoner og i 7.x-grenen. Hver har fordeler og ulemper, og det er viktig å kjenne dem for å velge riktig verktøy.
Startjobb: jobber i uavhengige prosesser
cmdleten Start-Job Den finnes i alle versjoner av PowerShell og starter arbeide i separate prosesser, hver med sin egen instans av PowerShellDette gir sterk isolasjon, men det har mye overhead: å opprette nye prosesser, serialisere og deserialisere objekter tilbake, osv.
I mange tilfeller kan det oppstå en sekvensiell løkke raskere enn å misbruke Start-Jobspesielt når oppgavene er korte eller returnerer store mengder komplekse data. Videre, Start-Job inkluderer ikke som standard en ThrottleLimitDerfor må du selv styre hvor mange jobber som kjøres samtidig.
Start-ThreadJob: trådbaserte jobber
Modulen Trådjobb gir cmdlet-en Start-ThreadJobsom bruker runspaces for å opprette lettere jobber enn de hos Start-JobVed å dele prosessen unngås mange av tapene av typeinformasjon forårsaket av serialisering mellom prosesser.
I PowerShell 7 og senere versjoner er ThreadJob-modulen allerede inkludert, mens i Windows PowerShell 5.1 kan installeres fra PowerShell-galleriet. Start-ThreadJob støtter parameteren ThrottleLimitDette lar deg kontrollere hvor mange av disse jobbene som kjøres samtidig, slik at du ikke bruker opp systemressursene.
PowerShell-kjøreområder og SDK
For avanserte scenarier kan du jobbe direkte med navneområdet System.Management.Automation.Runspaces fra PowerShell SDK. Dette lar deg lage din egen svært fine parallellitetslogikk, og kontrollere administrasjonen av kjøreområder, pooler, synkronisering osv. Faktisk begge deler ForEach-Object -Parallel som Start-ThreadJob De er internt avhengige av runspaces.
Denne tilnærmingen gir maksimal fleksibilitet og potensiell ytelse , men den innebærer også mer kompleksitet og "rørlegger"-kode. I mange tilfeller dekker de innebygde cmdletene (ForEach-Object-Parallel og Start-ThreadJob) de fleste behov, og eliminerer behovet for slik kompleksitet.
Arbeidsflyter og foreach -Parallel i Windows PowerShell
Windows PowerShell 5.1 hadde funksjonaliteten til å ArbeidsflytEn spesiell type skript designet for langvarige oppgaver, med mulighet for å sette på pause, gjenoppta og kjøre parallelt. Innenfor en arbeidsflyt kan konstruksjonen brukes foreach -ParallelAt utfør skriptblokken én gang for hvert element i en samling samtidig.
Den grunnleggende syntaksen til foreach -Parallel det er noe sånt som foreach -Parallel ($item in $collection) { ... }, Og Det er bare gyldig innenfor en arbeidsflytElementene i samlingen behandles parallelt, mens kommandoer innenfor blokken utføres de sekvensielt for hvert element (selv om det også kan kombineres med blokker) parallel { ... } innvendig).
Arbeidsflyter, derimot, De er ikke tilgjengelige i PowerShell 7 eller nyere versjoner.De anbefales heller ikke for nyutvikling. Likevel er det verdt å forstå syntaksen og konseptet hvis du vedlikeholder eldre skript som bruker dem. foreach -Parallel, siden oppførselen er forskjellig fra både vanlig foreach og ForEach-Object-Parallel.
Begrens samtidighet med ThrottleLimit
Bare fordi noe kan kjøre parallelt, betyr det ikke at det vil være raskere. Faktisk kan svært I/O-intensive applikasjoner eller lette skript lide ytelsestap hvis det er for mange samtidige oppgaver , enten det skyldes CPU-, disk- eller nettverksoverbelastning, eller rett og slett overheaden ved å administrere så mange tråder samtidig.
For å balansere dette aspektet, Start-ThreadJob y ForEach-Object -Parallel De tilbyr parameteren ThrottleLimitDette angir maksimalt antall jobber eller iterasjoner som kan kjøres samtidig. Hvis du prøver å starte flere jobber enn tillatt, vil de bli satt i kø til én er ferdig og frigjør plass.
Siden PowerShell 7.1, ForEach-Object -Parallel gjenbruker kjøreområder fra et standardbasseng, og verdien av ThrottleLimit angir størrelsen på den poolen. Standardverdien er 5, som vanligvis er et godt utgangspunkt. Det finnes også en modifikator for å opprette et nytt runspace for hver iterasjon (UseNewRunspace), men det lønner seg bare når du trenger veldig sterk isolasjon og tåler mer overbelastning.
På den annen side cmdlet Start-Job mangler ThrottleLimitDerfor, hvis du ikke kontrollerer det manuelt, kan du ende opp med dusinvis eller hundrevis av PowerShell-prosesser som kjører samtidig, med tilsvarende innvirkning på ytelse og minne.
Måling av ytelsen til ulike tilnærminger
En fornuftig måte å bestemme hvilken parallellitetsmodell som skal brukes på er Mål faktiske utførelsestider for brukstilfellet dittEn funksjon som Measure-Parallel, som sammenligner hastigheten til flere tilnærminger: Start-Job, Start-ThreadJob, ForEach-Object -Parallel y Start-Process.
Denne funksjonen oppretter en simulert samling av inndata (f.eks. ZIP-filnavn), genererer flere "batcher" eller arbeidsøkter, og måler for hver tilnærming hvor lang tid det tar å behandle et gitt antall elementer, med en batchstørrelse konfigurert av BatchSize og totalt verk JobCountDen tilpasser også de tilgjengelige tilnærmingene avhengig av PowerShell-versjonen og tilstedeværelsen eller fraværet av Start-ThreadJob.
Innsiden, Measure-Parallel konfigurer en kjørbar fil (som cmd.exe o shmed en liste over argumenter, og definerer en hashtabell med implementeringer som er spesifikke for hver tilnærming. Measure-Command beregne den totale tiden Ved å behandle alle batcher for hvert alternativ, kan du se detaljerte meldinger med -Verbose Og til slutt returnerer den et objekt med et sammendrag av tider, slik at de enkelt kan sammenlignes.
Kjører noe sånt som Measure-Parallel -Approach All -BatchSize 5 -JobCount 20 -Verbose På en Windows-datamaskin med PowerShell 7.5.1 observeres det at i det spesifikke tilfellet, Start-Process få kortest mulig tidtett fulgt av ForEach-Object -Parallel y Start-ThreadJobMens Start-Job Det viser seg å være det tregeste alternativet på grunn av den store overheaden som er involvert i å starte PowerShell-barnprosesser.
Parallelisme og «tradisjonelle» foreach-løkker
Noe som ofte skjer i praksis er at når du migrerer eldre arbeidsflyter og skript til PowerShell 7, begynner du å erstatte manuelle kjøreområder, tredjepartsmoduler som PoshRSJob eller arbeidsflyter gjennom verktøyene som allerede er integrert, som for eksempel Start-ThreadJob y ForEach-Object -ParallelDette forenkler koden og reduserer antallet eksterne avhengigheter. Det er også vanlig å integrere automatiseringsløsninger som PowerShell DSC.
Det finnes administratorer som brukte manuelle runspace-pooler, men som begynte å oppleve ustabil oppførsel (for eksempel skript som ville avslutte tilfeldig i produksjonsmiljøer), og som, stilt overfor vanskeligheten med å feilsøke disse problemene, har valgt å redusere kompleksiteten og stole på integrerte parallelle funksjoner som allerede er testet og støttet av PowerShell-teamet selv.
Avanserte teknikker med foreach: nesting, filtrering og feilhåndtering
Utover parallellismen, løkkene foreach lar deg bygge veldig rik logikk når du setter sammen setninger if, løkkehesting og unntakshåndtering.
Innenfor foreach Det er vanlig å bruke betingede if å behandle elementene annerledes basert på egenskapene deres. For eksempel å iterere gjennom alle filene i en katalog og bare handle på de som overstiger en viss størrelse, kopiere dem til et annet sted, komprimere dem eller slette dem hvis de oppfyller visse alderskrav.
Du kan også neste løkker foreach til generere kombinasjoner av elementer fra flere samlingerFor eksempel testparameterlister, kartesiske produkter mellom tall og bokstaver, eller todimensjonale datatraverseringer. I dette tilfellet er det avgjørende å ikke bruke samme variabelnavn på nytt i de indre og ytre løkkene for å unngå forvirring og subtile feil.
Angående feilhåndtering, hvis en foreach møter et ubehandlet unntak, Det er mulig at hele skriptet stopper opp.noe som ikke alltid er ønskelig. Den vanlige måten å styrke løkken på er å pakke den interne logikken inn i en blokk. try { ... } catch { ... }Så hvis noe går galt med et bestemt element, kan du logge feilen og fortsette med resten av samlingen uten å stoppe hele kjøringen.
Vanlige feil og beste praksis med foreach
Det finnes noen vanlige fallgruver når man jobber med foreach-løkker i PowerShell . En av dem er å bruke samme variabelnavn i nestede løkker, noe som fører til at den indre verdien overskriver den ytre og gir uforutsigbare resultater. Løsningen er enkel: bruk forskjellige navn for hvert hekningsnivå.
En annen vanlig feil er misbruke break uten å tenke det gjennomDette fører til at løkken avsluttes før hele samlingen er behandlet, når hensikten rett og slett var å hoppe over visse elementer. I mange tilfeller continue Det er mer passende fordi det lar deg hoppe over bestemte iterasjoner uten å forlate løkken helt.
Du må også være forsiktig med uendelige løkker, spesielt når du endrer den samme samlingen du itererer over . Hvis du legger til elementer i samme array uten kontroll under iterasjonen, er det lett å ende opp med en løkke som aldri fullføres. En veldig nyttig teknikk er å iterere over en kopi av den opprinnelige samlingen, og gjøre de nødvendige endringene i den "ekte" versjonen.
Når det gjelder generell beste praksis, er det vanligvis tilrådelig bruke ForEach-Object for data som kommer inn via pipeline, holde løkkeinnholdet så lesbart som mulig ved hjelp av hjelpefunksjoner, og bruke konsekvent innrykk og kommentarer for å forenkle skriptvedlikehold over tid.
Mestre de ulike foreach-variantene og deres parallellitetsalternativer i PowerShell, fra ForEach-Object -Parallel y Start-ThreadJob selv de gamle foreach -Parallel I arbeidsflyter lar det deg designe mye raskere og mer robuste skript, og velg i hvert tilfelle om du er interessert i å prioritere ytelse, minne, utdatarekkefølge eller kodens enkelhet, i stedet for alltid å begrense deg til den tradisjonelle sekvensielle løkken.
Lidenskapelig forfatter om verden av bytes og teknologi generelt. Jeg elsker å dele kunnskapen min gjennom å skrive, og det er det jeg skal gjøre i denne bloggen, vise deg alle de mest interessante tingene om dingser, programvare, maskinvare, teknologiske trender og mer. Målet mitt er å hjelpe deg med å navigere i den digitale verden på en enkel og underholdende måte.
Indeks
- 1 Looper i PowerShell: utgangspunktet
- 2 foreach vs. ForEach-Object vs. ForEach-metoden
- 3 Andre typer løkker: for, mens, do-while, do-until
- 4 Fin flytkontroll: pause, fortsett og returner
- 5 ForEach-Object-Parallel i PowerShell 7
- 6 Andre alternativer for parallellkjøring i PowerShell
- 7 Begrens samtidighet med ThrottleLimit
- 8 Måling av ytelsen til ulike tilnærminger
- 9 Parallelisme og «tradisjonelle» foreach-løkker
- 10 Avanserte teknikker med foreach: nesting, filtrering og feilhåndtering
- 11 Vanlige feil og beste praksis med foreach