Multi-threaded verwerking gebruiken in bash-scripts

0
157
Aleksey Mnogosmyslov/Shutterstock.com< /figuur>

Multi-threaded programmeren is altijd al interessant geweest voor ontwikkelaars om de prestaties van applicaties te verbeteren en het gebruik van bronnen te optimaliseren. Deze gids laat je kennismaken met de basisprincipes van Bash multi-threaded coderen.

Wat is multi-threaded programmeren?

Een foto zegt meer dan duizend woorden, en dit geldt als het gaat om het tonen van het verschil tussen single (1) thread-programmering en multi-threaded (>1) programmering in Bash:

sleep 1 sleep 1 &amp ; slaap 1

Onze eerste multi-threaded programmeer setup of mini one-liner script had niet eenvoudiger kunnen zijn; in de eerste regel slapen we één seconde met het sleep 1-commando. Wat de gebruiker betreft, voerde een enkele thread een enkele slaap van één seconde uit.

In de tweede regel hebben we twee slaapopdrachten van één seconde. We voegen ons bij hen door een & scheidingsteken, dat niet alleen fungeert als scheidingsteken tussen de twee slaapcommando's, maar ook als een indicator voor Bash om het eerste commando in een achtergrondthread te starten.

Normaal gesproken zou men een commando beëindigen door een puntkomma (;). Als u dit doet, wordt de opdracht uitgevoerd en gaat u pas verder met de volgende opdracht die achter de puntkomma staat. Bijvoorbeeld slaap 1 uitvoeren; slaap 1 zou iets meer dan twee seconden duren – precies één seconde voor het eerste commando, één seconde voor het tweede, en een kleine hoeveelheid systeemoverhead voor elk van de twee commando's.

In plaats van een opdracht te beëindigen met een puntkomma, kun je echter andere opdrachtbeëindigers gebruiken die Bash herkent, zoals &, && en ||. De &&; syntaxis is helemaal niet gerelateerd aan multi-threaded programmeren, het doet dit gewoon; ga verder met het uitvoeren van de tweede opdracht alleen als de eerste opdracht succesvol was. De || is het tegenovergestelde van && en zal de tweede opdracht alleen uitvoeren als de eerste opdracht is mislukt.

Terugkerend naar multi-threaded programmeren, met & omdat onze opdrachtbeëindiger een achtergrondproces zal starten dat de opdracht ervoor uitvoert. Het gaat dan onmiddellijk verder met het uitvoeren van de volgende opdracht in de huidige shell terwijl het achtergrondproces (thread) zelf wordt uitgevoerd.

Advertentie

In de uitvoer van de opdracht kunnen we zien dat een achtergrondproces wordt gestart (zoals aangegeven door [1] 445317 waarbij 445317 de proces-ID of PID is van het zojuist gestarte achtergrondproces en [1] is aangegeven dat dit ons eerste achtergrondproces is ) en het wordt vervolgens beëindigd (zoals aangegeven door [1]+ Done sleep 1).

Als u een extra voorbeeld van procesafhandeling op de achtergrond wilt zien, raadpleeg dan onze Bash Automation and Scripting Basics (deel 3) artikel. Daarnaast kunnen Bash Process Termination Hacks interessant zijn.

Laten we nu bewijzen dat we in feite twee slaapprocessen tegelijkertijd uitvoeren:

time sleep 1; echo 'klaar' tijd $(slaap 1 & slaap 1); echo 'klaar'

Hier starten we ons slaapproces onder de tijd en we kunnen zien hoe onze single-threaded-opdracht precies 1.003 seconden liep voordat onze opdrachtregelprompt werd geretourneerd.

In het tweede voorbeeld duurde het echter ongeveer dezelfde tijd (1,005 seconden), hoewel we twee perioden (en processen) van slaap aan het uitvoeren waren, hoewel niet opeenvolgend. Opnieuw gebruikten we een achtergrondproces voor het eerste slaapcommando, wat leidde tot (semi-)parallelle uitvoering, d.w.z. multi-threaded.

Advertentie

We gebruikten ook een subshell-wrapper ($(…)) rond onze twee slaapcommando's om ze onder tijd te combineren. Zoals we kunnen zien, wordt onze voltooide uitvoer in 1,005 seconden weergegeven en dus moeten de twee slaap 1-opdrachten tegelijkertijd zijn uitgevoerd. Interessant is de zeer kleine toename van de totale verwerkingstijd (0,002 seconden), die gemakkelijk kan worden verklaard door de tijd die nodig is om een ​​subshell te starten en de tijd die nodig is om een ​​achtergrondproces te starten.

Multi-threaded (en achtergrond) procesbeheer

In Bash omvat multi-threaded codering normaal gesproken achtergrondthreads van een hoofdscript met één regel of een volledig Bash-script. In wezen zou men kunnen denken aan multi-threaded codering in Bash als het starten van verschillende achtergrondthreads. Wanneer men begint te coderen met behulp van meerdere threads, wordt het snel duidelijk dat dergelijke threads meestal enige behandeling vereisen. Neem bijvoorbeeld het fictieve voorbeeld waarin we vijf gelijktijdige perioden (en processen) van slaap beginnen in een Bash-script;

#!/bin/bash slaap 10 & slaap 600 & slaap 1200 & slaap 1800 & slaap 3600 &

Als we het script starten (nadat we het uitvoerbaar hebben gemaakt met chmod +x rest .sh), zien we geen uitvoer! Zelfs als we taken uitvoeren (het commando dat alle lopende achtergrondtaken laat zien), is er geen uitvoer. Waarom?

De reden is dat de shell die werd gebruikt om dit script te starten (dwz de huidige shell) niet dezelfde shell is (noch dezelfde thread; om te gaan denken in termen van subshells als threads in en op zichzelf) die de eigenlijke slaap uitvoerde commando's of plaatste ze op de achtergrond. Het was eerder de (sub)shell die werd gestart toen ./rest.sh werd uitgevoerd.

Laten we ons script veranderen door jobs in het script toe te voegen. Dit zorgt ervoor dat taken worden uitgevoerd vanuit de (sub)shell waar het relevant is, dezelfde als waar de perioden (en processen) van slaap zijn gestart.

Deze keer kunnen we de lijst met achtergrondprocessen zien die worden gestart dankzij de opdracht jobs op het einde van het script. We kunnen ook hun PID’s (Process Identifiers) zien. Deze PID's zijn erg belangrijk als het gaat om het afhandelen en beheren van achtergrondprocessen.

Advertentie

Een andere manier om de procesidentificatie op de achtergrond te verkrijgen, is door er onmiddellijk naar te vragen nadat een programma/proces op de achtergrond is geplaatst:

#!/bin/bash slaap 10 & echo ${!} slaap 600 & echo ${!} slaap 1200 & echo ${!} slaap 1800 & echo ${!} slaap 3600 & echo ${!}

Vergelijkbaar met ons jobs-commando (met nieuwe PID’s nu we ons rest.sh-script opnieuw hebben opgestart), dankzij de echo van de Bash ${!}-variabele, zullen we nu de vijf PID's bijna zien worden weergegeven onmiddellijk nadat het script start: de verschillende slaapprocessen werden achter elkaar in achtergrondthreads geplaatst.

Het wait-commando

Zodra we onze achtergrond hebben gestart processen, hoeven we niets anders te doen dan te wachten tot ze zijn voltooid. Wanneer echter elk achtergrondproces een complexe subtaak uitvoert en we het hoofdscript (dat de achtergrondprocessen heeft gestart) nodig hebben om de uitvoering te hervatten wanneer een of meer van de achtergrondprocessen worden beëindigd, hebben we aanvullende code nodig om dit af te handelen.

Laten we ons script nu uitbreiden met het wait-commando om onze achtergrondthreads af te handelen:

#!/bin/bash sleep 10 & T1=${!} slaap 600 & T2=${!} slaap 1200 & T3=${!} slaap 1800 & T4=${!} slaap 3600 & T5=${!} echo “Dit script startte 5 achtergrondthreads die momenteel worden uitgevoerd met PID's ${T1}, ${T2}, ${T3}, ${T4}, ${T5}.” wacht ${T1} echo “Draad 1 (slaap 10) met PID ${T1} is voltooid!” wacht ${T2} echo “Thread 2 (slaap 600) met PID ${T2} is voltooid!”

Hier hebben we ons script uitgebreid met twee wachtcommando's die wachten op de PID die aan de eerste is gekoppeld en tweede threads om te beëindigen. Na 10 seconden bestaat onze eerste thread en worden we hiervan op de hoogte gesteld. Stap voor stap zal dit script het volgende doen: start vijf threads op bijna hetzelfde moment (hoewel het starten van de threads zelf nog steeds opeenvolgend en niet parallel is), waarbij elk van de vijf slaap's parallel zal worden uitgevoerd.< /p>

Het hoofdscript rapporteert vervolgens (opeenvolgend) over de gemaakte thread en wacht vervolgens tot het proces-ID van de eerste thread wordt beëindigd. Wanneer dat gebeurt, zal het achtereenvolgens rapporteren over het einde van de eerste thread en beginnen met wachten tot de tweede thread is voltooid, enz.

Advertentie

Het gebruik van de Bash-idioom &, ${!} en de wait geeft ons een grote flexibiliteit als het gaat om het parallel uitvoeren van meerdere threads (als achtergrondthreads) in Bash.

Afronden

In dit artikel hebben we de basisprincipes van Bash multi-threaded scripting onderzocht. We hebben de procesoperator op de achtergrond (&) geïntroduceerd met behulp van enkele eenvoudig te volgen voorbeelden die zowel enkel- als meerdraads slaapcommando's tonen. Vervolgens hebben we gekeken hoe we achtergrondprocessen kunnen afhandelen via de veelgebruikte Bash-idioom ${!} en wachten. We hebben ook de opdracht jobs onderzocht om actieve achtergrondthreads/processen te zien.

Als je dit artikel met plezier hebt gelezen, bekijk dan ons artikel Bash Process Termination Hacks.