"Threads" in Shell-Scripts

C

cmg

Mitglied
Hi,

ich habe ein Bildbearbeitungs-Script. Das hat halt unmengen von converts, usw und dauert daher ewig bis es abgearbeitet ist. Die einzelnen Abschnitte könnte man theoretisch auch parallel machen. Nur ist das irgendwie möglich? Im Prinzip ists ja nichts anderes, als wenn ich ein Teil in ein anderes Script auslager und beide gleichzeitig starte... gibts da was in der Richtig?
 
Du kannst auch einzelne Teile eines Skripts in Funktionen auslagern und diese im Hintergrund ausführen lassen:
Code:
function waitFoo { sleep 3; echo "foo"; }
function bar { echo "bar"; }
waitFoo & bar &
Gibt dann natürlich diese "[1]+ Done"-like Meldungen. Gilt übrigens erstmal (faulerweise) nur für bash. (ksh und sh sollten das aber auch können. csh bin ich mir schon nicht mehr so sicher..)
 
Du kannst auch einzelne Teile eines Skripts in Funktionen auslagern und diese im Hintergrund ausführen lassen:
Code:

function waitFoo { sleep 3; echo "foo"; }
function bar { echo "bar"; }
waitFoo & bar &

Gibt dann natürlich diese "[1]+ Done"-like Meldungen. Gilt übrigens erstmal (faulerweise) nur für bash. (ksh und sh sollten das aber auch können. csh bin ich mir schon nicht mehr so sicher..)

Das hat mit Threading aber nix zu tun und der Geschwindigkeitszuwachs wird um den Nullpunkt herum oszillieren.

at cmg:

1. Es hängt von deiner Applikation ab, ob Threading was bringt.

2. Spielt das aber auch keine Rolle, weil die Bash keine "echtes" Threading kann.

Wenn es nicht zuviel Aufwand ist, würde ich mir überlegen, die Script-Funktionalität in einer Hochsprache zu implementieren, die Threading kann.
 
Ja ich weiß, war auch mehr auf diese Aussage und halt die Tatsache, dass dieser Thread in shell-Skripte-Subforum plaziert ist gemünzt:
Im Prinzip ists ja nichts anderes, als wenn ich ein Teil in ein anderes Script auslager und beide gleichzeitig starte

[slightlyOffTopic]
Aber mich würde mal interessieren, inwiefern Threads performance-Vorteile bringen sollen. Weil das switchen zwischen verschiedenen Threads für die CPU schneller geht als zwischen verschiedenen (separaten) Prozessen? Ansonsten ist es doch eigentlich nur die Idee einen blockierenden Vorgang, der die CPU bis zu einem gewissen Grad "idlen" lässt, in mehrfacher Ausführung parallel zu starten, oder? (Zumindest ist das hier imho so gemeint.)
Außerdem: fork() startet doch auch einen separaten Prozess (und kopiert auch noch den kompletten Speicher des Prozesses dabei). Sollte dann nicht quasi als "best practice" vom fork()en (zumindest bei rechenintensiven Applikationen) zu Gunsten von Threads abgeraten werden? (Was ich bisher so noch nicht mitbekommen habe, aber man lernt ja nie aus.)
[/slightlyOffTopic]
 
Aber mich würde mal interessieren, inwiefern Threads performance-Vorteile bringen sollen.

Bevor ich mir jetzt die Finger wund tippe - hier mal eine sehr anschauliche Erklärung aus "Java ist auch eine Insel" (gilt aber für Threading allgemeint):

10.1.2 Wie parallele Programme die Geschwindigkeit steigern können Zur nächsten ÜberschriftZur vorigen Überschrift

Auf den ersten Blick ist nicht ersichtlich, warum auf einem Einprozessorsystem die nebenläufige Abarbeitung eines Programms geschwindigkeitssteigernd sein kann. Betrachten wir daher ein Programm, das eine Folge von Anweisungen ausführt. Die Programmsequenz dient zum Visualisieren eines Datenbank-Reports. Zunächst wird ein Fenster zur Fortschrittsanzeige dargestellt. Anschließend werden die Daten analysiert und der Fortschrittsbalken kontinuierlich aktualisiert. Schließlich werden die Ergebnisse in eine Datei geschrieben. Die Schritte sind:
1. Baue ein Fenster auf.

2. Öffne die Datenbank vom Netz-Server, und lies die Datensätze.

3. Analysiere die Daten, und visualisiere den Fortschritt.

4. Öffne die Datei, und schreibe den erstellten Report.


Was auf den ersten Blick wie ein typisches sequenzielles Programm aussieht, kann durch geschickte Parallelisierung beschleunigt werden.

Zum Verständnis ziehen wir noch einmal den Vergleich mit Prozessen. Nehmen wir an, auf einer Einprozessormaschine sind fünf Benutzer angemeldet, die im Editor Quelltext tippen und hin und wieder den Java-Compiler bemühen. Die Benutzer würden vermutlich die Belastung des Systems durch die anderen nicht mitbekommen, denn Editor-Operationen lasten den Prozessor nicht aus. Wenn Dateien kompiliert und somit vom Hintergrundspeicher in den Hauptspeicher transferiert werden, ist der Prozessor schon besser ausgelastet, doch geschieht dies nicht regelmäßig. Im Idealfall übersetzen alle Benutzer nur dann, wenn die anderen gerade nicht übersetzen – im schlechtesten Fall möchten natürlich alle Benutzer gleichzeitig übersetzen.

Übertragen wir die Verteilung auf unser Problem, nämlich wie der Datenbank-Report schneller zusammengestellt werden kann. Beginnen wir mit der Überlegung, welche Operationen parallel ausgeführt werden können:

* Das Öffnen von Fenster, Ausgabedatei und Datenbank kann parallel geschehen.

* Das Lesen neuer Datensätze und das Analysieren alter Daten kann gleichzeitig erfolgen.

* Alte analysierte Werte können während der neuen Analyse in die Datei geschrieben werden.

Wenn die Operationen wirklich parallel ausgeführt werden, lässt sich bei Mehrprozessorsystemen ein enormer Leistungszuwachs verzeichnen. Doch interessanterweise ergibt sich dieser auch bei nur einem Prozessor, was in den Aufgaben begründet liegt. Denn bei den gleichzeitig auszuführenden Aufgaben handelt es sich um unterschiedliche Ressourcen. Wenn die grafische Oberfläche das Fenster aufbaut, braucht sie dazu natürlich Rechenzeit. Parallel kann die Datei geöffnet werden, wobei weniger Prozessorleistung gefragt ist, da die vergleichsweise träge Festplatte angesprochen wird. Das Öffnen der Datenbank wird auf den Datenbank-Server im Netzwerk abgewälzt. Die Geschwindigkeit hängt von der Belastung des Servers und des Netzes ab. Wenn anschließend die Daten gelesen werden, muss die Verbindung zum Datenbank-Server natürlich stehen. Daher sollten wir zuerst die Verbindung aufbauen.

Ist die Verbindung hergestellt, lassen sich über das Netzwerk Daten in einen Puffer holen. Der Prozessor wird nicht belastet, vielmehr der Server auf der Gegenseite und das Netzwerk. Während der Prozessor also vor sich hindöst und sich langweilt, können wir ihn besser beschäftigen, indem er alte Daten analysiert. Wir verwenden hierfür zwei Puffer. In den einen lädt ein Thread die Daten, während ein zweiter Thread die Daten im anderen Puffer analysiert. Dann werden die Rollen der beiden Puffer getauscht. Jetzt ist der Prozessor beschäftigt. Er ist aber vermutlich fertig, bevor die neuen Daten über das Netzwerk eingetroffen sind. In der Zwischenzeit können die Report-Daten in den Report geschrieben werden; eine Aufgabe, die wieder die Festplatte belastet und weniger den Prozessor.

Wir sehen an diesem Beispiel, dass durch hohe Parallelisierung eine Leistungssteigerung möglich ist, da die bei langsamen Operationen anfallenden Wartezeiten genutzt werden können. Langsame Arbeitsschritte lasten den Prozessor nicht aus, und die anfallende Wartezeit vom Prozessor beim Netzwerkzugriff auf eine Datenbank kann für andere Aktivitäten genutzt werden.

Ich hatte schon Fälle, wo ordentlich implementiertes Threading eine Leistungssteigerung von 90% ausgemacht hat.
 
k. IMHO ist das allerdings erstmal keine Argumentation spezifisch für Threads, sondern für Multitasking. Wobei Multitasking durch Threads oder separate Prozesse erreicht werden kann. Wenn man der wikipedia glauben darf (was man selbstredend anzweifeln kann, aber ich hab jetzt grad kein Bock groß für ne OT-Diskussion zu recherchieren), sollten threads im Gegensatz zu separaten Prozessen den Kohl auch nicht Fett machen:
Systems like Windows NT and OS/2 are said to have "cheap" threads and "expensive" processes; in other operating systems there is not so great a difference.
von hier.
ANYWHO, wird hier zu OT. Für das Thema können wir ja einen separaten Thread (oder Prozess *schenkelklopf*) aufmachen, oder es halt einfach an dieser Stelle sein lassen.
 
oder es halt einfach an dieser Stelle sein lassen.

Ich stimme zu, aber ein letzter Nachtrag sei mir noch gestattet....:devil:

k. IMHO ist das allerdings erstmal keine Argumentation spezifisch für Threads

Natürlich nicht, deshalb schrieb ich ja:

1. Es hängt von deiner Applikation ab, ob Threading was bringt.

Threading will intelligent gemacht sein, sonst bringt es nix!

So, aber nu EOOT (== end of off-topic....:) )
 
Du kannst auch einzelne Teile eines Skripts in Funktionen auslagern und diese im Hintergrund ausführen lassen:
Code:
function waitFoo { sleep 3; echo "foo"; }
function bar { echo "bar"; }
waitFoo & bar &
Gibt dann natürlich diese "[1]+ Done"-like Meldungen. Gilt übrigens erstmal (faulerweise) nur für bash. (ksh und sh sollten das aber auch können. csh bin ich mir schon nicht mehr so sicher..)

Jau, das funktioniert bei mir wunderbar.
Allerdings gibt es ein Problem.
Ich habe eben 7 Unterfunktionen die bisher einfach nacheinander ausgeführt wurden. Dadurch war die Ausgabe natürlich auch eindeutig. Wenn ich nun alle 7 Funktionen auf einmal ausführe bekomme ich die Ausgaben die jede Funktion macht eben nicht in der Reihenfolge wie vorher, sondern durcheinander.

Deshalb wollte ich die Ausgabe nicht direkt machen, sondern einfach nach Abarbeitung von allen 7 Funktionen alles ausgeben, damit ich wieder die Reihenfolge sichergestellt habe.

Nur wie bekomme ich den Wert der Variablen, der in den Funktionen gesetzt wird, wieder in mein Ursprungsprogramm rein?
 
Geht nicht direkt, da Funktionen in einer Subshell gestartet werden und man von der Elternshell nichts vom Skope der Subshell sehen kann. (Variablentechnisch)
Du kannst allerdings am Ende der Funktion Output produzieren und den dann in der Elternshell capturen:
Code:
function foo { echo "foo"; }
function bar { sleep 3; echo "bar"; }
fooOutput="$(foo)" &
barOutput="$(bar)" &
Beachte, dass nun die (der/das?) Variable-Assignment im Hintergrund ausgeführt wird, nicht der Funktionsaufruf selber.
Wenn du mehrere Variablen setzen willst, kannst du das zum Beispiel über ein Array machen:
Code:
function baz { echo "element1 element2 tuedelue"; }
array=( $(baz) ) &
echo ${array[2]} # gibt "tuedelue" aus
Beachte hier, dass ich die Double-Quotes um $(baz) ausgelassen hab.
 
Ich habe eben 7 Unterfunktionen die bisher einfach nacheinander ausgeführt wurden. Dadurch war die Ausgabe natürlich auch eindeutig. Wenn ich nun alle 7 Funktionen auf einmal ausführe bekomme ich die Ausgaben die jede Funktion macht eben nicht in der Reihenfolge wie vorher, sondern durcheinander.

Du hast die Diskussion nicht verstanden. Oder nicht verstehen wollen.

Also nochmal:


Du wirst so keinen Geschwindigkeitszuwachs feststellen können.


Du hast 2 Möglichkeiten:

- Dein Script optimieren (Und nein, irgendwas in den Hintergrund schieben hat damit nix zu tun)
- Feststellen, ob Threading bei dir was bringt, und dann deine Applikation anpassen / umschreiben.

War das jetzt deutlich genug?
 
@Gott_in_schwarz: danke, werde es mal teste

@supersucker: ich habe es selber ausprobiert und spare dadurch über 50% an Zeit ein... also kann irgendwann an deiner Theorie net stimmen.
 
@supersucker: ich habe es selber ausprobiert und spare dadurch über 50% an Zeit ein... also kann irgendwann an deiner Theorie net stimmen.

Äh, ja, das ist keine Theorie, sondern die Grundlagen der Informatik........

Poste doch einfach mal:

* Was du vorher gemacht hast
* Was der derzeitige Stand ist

und dann sehen wir weiter!

Und wenn das hier:

@supersucker: ich habe es selber ausprobiert und spare dadurch über 50% an Zeit ein... also kann irgendwann an deiner Theorie net stimmen.

stimmen sollte, ist was an deiner Applikation falsch, nicht aber an dem Modell........
 
* Was du vorher gemacht hast

fct1(){
#30 converts und 10 vergleiche mit cmp
}

fct2(){
#10 converts und 10 vergleiche mit cmp
}

fct3(){
#10 converts und 10 vergleiche mit cmp
}

fct4(){
#2 converts und 1 vergleiche mit cmp
}

fct5(){
#30 converts und 10 vergleiche mit cmp
}

fct6(){
#12 converts und 25 vergleiche mit cmp
}

fct7(){
#4 converts und 10 vergleiche mit cmp
}

die "main" sah dann so aus:

fct1
fct2
fct3
fct4
fct5
fct6
fct7

Ausgabe von in den Funktionen fand in eine Datei statt, dat wars.



* Was der derzeitige Stand ist

selbe wie oben nur mit

fct1 &
fct2 &
fct3 &
fct4 &
fct5 &
fct6 &
fct7 &

Und die Ergebnisse trudeln nun je nach abgearbeiter Funktion ein. Also fct4 ist beispielsweise als erstes fertig. Ist ja auch nichts drin...


EDIT:
So, ich habe es nun hinbekommen. Allerdings habe ich es nicht mit dem "Output capturen" hinbekommen, zumindest nicht schneller als vorher.
Ich habe nun die trivialste Lösung gewählt. Jede Fkt hat eine extra Output-Datei und zum schluss merge ich die einfach. Dadurch sind alle Probleme gelöst.
Danke an alle!
 
Zuletzt bearbeitet:

Ähnliche Themen

script wird von Cron ausgeführt, aber einige Befehle nicht

Zurück
Oben