So verwenden Sie die Multithreaded-Verarbeitung in Bash-Skripten

0
147
Aleksey Mnogosmyslov/Shutterstock.com< /figure>

Multithread-Programmierung war schon immer für Entwickler von Interesse, um die Anwendungsleistung zu steigern und die Ressourcennutzung zu optimieren. Dieser Leitfaden führt Sie in die Grundlagen der Bash-Multithread-Codierung ein.

Was ist Multithread-Programmierung?

Ein Bild sagt mehr als tausend Worte, und dies gilt, wenn es darum geht, den Unterschied zwischen Single-(1)-Thread-Programmierung und Multi-Threaded-Programmierung (>1) in Bash zu zeigen:

sleep 1 sleep 1 &amp ; Schlaf 1

Unser erstes Multithread-Programmier-Setup oder Mini-Einzeiler-Skript hätte nicht einfacher sein können; In der ersten Zeile schlafen wir eine Sekunde lang mit dem Befehl sleep 1 . Was den Benutzer betrifft, hat ein einzelner Thread einen einzelnen Schlaf von einer Sekunde ausgeführt.

In der zweiten Zeile haben wir zwei Ein-Sekunden-Schlafbefehle. Wir verbinden sie mit einem & Trennzeichen, das nicht nur als Trennzeichen zwischen den beiden Sleep-Befehlen fungiert, sondern auch als Indikator für die Bash, den ersten Befehl in einem Hintergrund-Thread zu starten.

Normalerweise würde man einen Befehl mit a . beenden Semikolon (;). Dies würde den Befehl ausführen und erst dann zum nächsten Befehl gehen, der hinter dem Semikolon aufgeführt ist. Beispiel: Ausführen von sleep 1; sleep 1 würde etwas mehr als zwei Sekunden dauern – genau eine Sekunde für den ersten Befehl, eine Sekunde für den zweiten und ein winziger System-Overhead für jeden der beiden Befehle.

Anstatt einen Befehl jedoch mit einem Semikolon zu beenden, kann man andere Befehlsterminatoren verwenden, die Bash erkennt, wie &, && und ||. Die && Syntax hat nichts mit Multithread-Programmierung zu tun, sie tut dies einfach; Fahren Sie mit der Ausführung des zweiten Befehls nur fort, wenn der erste Befehl erfolgreich war. Die || ist das Gegenteil von && und führt den zweiten Befehl nur aus, wenn der erste Befehl fehlgeschlagen ist.

Zurück zur Multithread-Programmierung mit & da unser Befehlsterminator einen Hintergrundprozess einleitet, der den vorhergehenden Befehl ausführt. Es fährt dann sofort mit der Ausführung des nächsten Befehls in der aktuellen Shell fort, während der Hintergrundprozess (Thread) alleine ausgeführt wird.

Werbung

In der Ausgabe des Befehls sehen wir, dass ein Hintergrundprozess gestartet wird (wie durch [1] 445317 angegeben, wobei 445317 die Prozess-ID oder PID des gerade gestarteten Hintergrundprozesses ist und [1] anzeigt, dass dies unser erster Hintergrundprozess ist ) und anschließend beendet (angezeigt durch [1]+ Done sleep 1).

Wenn Sie ein zusätzliches Beispiel für die Verarbeitung von Hintergrundprozessen sehen möchten, lesen Sie bitte unsere Grundlagen zur Bash-Automatisierung und Skripterstellung (Teil 3) Artikel. Außerdem könnten Bash Process Termination Hacks von Interesse sein.

Beweisen wir nun, dass wir effektiv zwei Sleep-Prozesse gleichzeitig ausführen:

time sleep 1; echo 'fertig' Zeit $(Schlaf 1 & Schlaf 1); echo 'fertig'

Hier starten wir unseren Sleep-Prozess unter Zeit und wir können sehen, wie unser Single-Thread-Befehl genau 1,003 Sekunden lang lief, bevor unsere Befehlszeilenaufforderung zurückgegeben wurde.

Im zweiten Beispiel dauerte es jedoch ungefähr die gleiche Zeit (1,005 Sekunden), obwohl wir zwei Schlafphasen (und Prozesse) ausgeführt haben, jedoch nicht nacheinander. Auch hier haben wir für den ersten sleep-Befehl einen Hintergrundprozess verwendet, der zu einer (semi-)parallelen Ausführung führt, d. h. multithreaded.

Werbung

Wir haben auch einen Subshell-Wrapper ($(…)) um unsere beiden Sleep-Befehle verwendet, um sie unter Zeit miteinander zu kombinieren. Wie wir sehen können, zeigt unsere fertige Ausgabe in 1,005 Sekunden an und daher müssen die beiden Befehle sleep 1 gleichzeitig ausgeführt worden sein. Interessant ist der sehr geringe Anstieg der Gesamtverarbeitungszeit (0,002 Sekunden), der sich leicht mit der Zeit zum Starten einer Subshell und der Zeit zum Initiieren eines Hintergrundprozesses erklären lässt.

Multithreaded- (und Hintergrund-)Prozessmanagement

In Bash umfasst Multithread-Codierung normalerweise Hintergrundthreads aus einem einzeiligen Hauptskript oder einem vollständigen Bash-Skript. Im Wesentlichen kann man sich Multithread-Codierung in Bash als das Starten mehrerer Hintergrundthreads vorstellen. Wenn man anfängt, mit mehreren Threads zu codieren, wird schnell klar, dass solche Threads normalerweise etwas Handhabung erfordern. Nehmen wir zum Beispiel das fiktive Beispiel, in dem wir fünf gleichzeitige Schlafphasen (und -prozesse) in einem Bash-Skript starten;

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

Wenn wir das Skript starten (nachdem es mit chmod +x rest ausführbar gemacht wurde) .sh), sehen wir keine Ausgabe! Selbst wenn wir Jobs ausführen (der Befehl, der alle laufenden Hintergrundjobs anzeigt), erfolgt keine Ausgabe. Warum?

Der Grund dafür ist, dass die Shell, die zum Starten dieses Skripts verwendet wurde (dh die aktuelle Shell), nicht dieselbe Shell (oder derselbe Thread; um in Begriffen von Subshells als Threads in und für sich zu denken) ist, die den eigentlichen Schlaf ausgeführt hat Befehle oder in den Hintergrund gestellt. Es war eher die (Sub-)Shell, die gestartet wurde, als ./rest.sh ausgeführt wurde.

Lassen Sie uns unser Skript ändern, indem wir Jobs innerhalb des Skripts hinzufügen. Dadurch wird sichergestellt, dass Jobs innerhalb der (Sub-)Shell ausgeführt werden, wo es relevant ist, die gleiche, in der die Schlafphasen (und Prozesse) gestartet wurden.

Dieses Mal sehen wir die Liste der Hintergrundprozesse, die dank des Jobs-Befehls gestartet werden at das Ende des Skripts. Wir können auch ihre PID’s (Process Identifiers) sehen. Diese PIDs sind sehr wichtig, wenn es um die Handhabung und Verwaltung von Hintergrundprozessen geht.

Werbung

Eine andere Möglichkeit, den Hintergrundprozess-Identifikator zu erhalten, besteht darin, ihn sofort abzufragen, nachdem ein Programm/Prozess in den Hintergrund versetzt wurde:

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

Ähnlich wie bei unserem jobs-Befehl (mit neuen PID’s jetzt, da wir unser rest.sh-Skript neu gestartet haben) werden wir dank der Echo-Variablen Bash ${!} jetzt fast sehen, wie die fünf PID’s angezeigt werden unmittelbar nach dem Start des Skripts: Die verschiedenen Schlafprozesse wurden nacheinander in Hintergrundthreads gelegt.

Der wait-Befehl

Sobald wir unseren Hintergrund gestartet haben Prozesse haben wir nichts weiter zu tun, als zu warten, bis sie abgeschlossen sind. Wenn jedoch jeder Hintergrundprozess eine komplexe Unteraufgabe ausführt und wir das Hauptskript (das die Hintergrundprozesse gestartet hat) benötigen, um die Ausführung fortzusetzen, wenn einer oder mehrere der Hintergrundprozesse beendet werden, benötigen wir zusätzlichen Code, um dies zu handhaben.

Erweitern wir unser Skript jetzt mit dem Wait-Befehl, um unsere Hintergrundthreads zu verarbeiten:

#!/bin/bash sleep 10 & T1=${!} Schlaf 600 & T2=${!} Schlaf 1200 & T3=${!} Schlaf 1800 & T4=${!} Schlaf 3600 & T5=${!} echo “Dieses Skript hat 5 Hintergrundthreads gestartet, die derzeit mit den PIDs ${T1}, ${T2}, ${T3}, ${T4}, ${T5} ausgeführt werden.” wait ${T1} echo “Thread 1 (sleep 10) mit PID ${T1} ist fertig!” wait ${T2} echo “Thread 2 (sleep 600) mit PID ${T2} ist fertig!”

Hier haben wir unser Skript um zwei Wartebefehle erweitert, die auf die an die erste angehängte PID warten und zweite Threads zum Beenden. Nach 10 Sekunden existiert unser erster Thread und wir werden darüber benachrichtigt. Schritt für Schritt führt dieses Skript Folgendes aus: Startet fünf Threads fast gleichzeitig (obwohl der Start der Threads selbst immer noch sequentiell und nicht parallel ist), wobei jeder der fünf Schlafphasen parallel ausgeführt wird.< /p>

Das Hauptskript berichtet dann (sequentiell) über den erstellten Thread und wartet anschließend auf die Beendigung der Prozess-ID des ersten Threads. Wenn das passiert, wird es nacheinander über die Beendigung des ersten Threads berichten und eine Wartezeit auf das Ende des zweiten Threads einleiten usw.

Werbung

Verwendung der Bash-Idiome &, ${!} und der Der wait-Befehl gibt uns große Flexibilität, wenn es darum geht, mehrere Threads parallel (als Hintergrund-Threads) in Bash auszuführen.

Zusammenfassung

In diesem Artikel haben wir die Grundlagen der Bash-Multithread-Skripterstellung untersucht. Wir haben den Hintergrundprozessoperator (&) anhand einiger leicht verständlicher Beispiele vorgestellt, die sowohl Single- als auch Multithread-Schlafbefehle zeigen. Als nächstes haben wir uns angeschaut, wie man Hintergrundprozesse über die häufig verwendeten Bash-Idiome ${!} handhabt und wartet. Wir haben auch den Befehl jobs untersucht, um laufende Hintergrund-Threads/-Prozesse anzuzeigen.

Wenn Ihnen das Lesen dieses Artikels gefallen hat, lesen Sie unseren Artikel zu Bash Process Termination Hacks.