Open Source im professionellen Einsatz

Newsletter abonnieren
Seite durchsuchen

HEFTARCHIV | NEWS | E-BIBLIOTHEK | VIDEO | BLOGS | WHITEPAPER | EVENTS | ACADEMY | ABO | SHOP

user friendly

  Home  »  Heft & Abo  »  Heftarchiv  »  2009  »  02  »  Parallelarbeit  

RSS-Feed der aktuellen News von Linux-Magazin Online Folgen Sie Linux-Magazin Online auf Twitter
Diesen Artikel druckenDiesen Artikel weiterempfehlen Diesen Artikel kommentieren Newsletter abonnieren
Share/Bookmark

Gleichmäßig ist günstig

Dieses Verarbeitungsschema ist dann optimal, wenn die durchschnittliche Verarbeitungszeit je Item nicht stark schwankt. Leider ist das nicht immer der Fall. Bei der Konvertierung mehrerer Musikstücke etwa könnte eine ungünstige Verteilung von langen und kurzen Stücken dazu führen, dass einzelne Prozesse deutlich eher fertig sind als andere.

Ein weiteres ungünstiges Beispiel ist die Konvertierung von Bildern aus digitalen Kameras. Manche Kameras erzeugen neben der RAW-Datei auch eine JPG- oder Thumbnail-Datei. Wenn nur jede zweite Datei im RAW-Format vorliegt und tatsächlich zu konvertieren ist, dann langweilt sich die Hälfte der Konvertierungsprozesse, weil das Verarbeitungsschema einem Prozess alle RAW- und dem anderen Prozess alle JPG-Dateien zuteilt.

Unabhängig von diesen Problemen kämpft diese Verarbeitungsmethode mit dem Umstand, dass nicht notwendigerweise alle Argumente im Vorhinein bekannt sind. Fallen die späteren Argumente des Skripts nacheinander an, wäre es ungünstig, so lange zu warten, bis alle erzeugt sind, um sie erst dann auf die Verarbeitungsprozesse zu verteilen. Die Lösung für dieses Problem verwendet stattdessen Worker-Prozesse und einen dynamischen Dispatcher.


Abbildung 2: Dispatcher und Worker-Prozesse kommunizieren über Pipes.

Der dynamische Dispatcher

Die Verarbeitungslogik sieht dabei so aus: Das Skript startet eine Reihe von Worker-Prozessen. Ein Dispatcher nimmt Aufträge entgegen und verteilt sie möglichst intelligent auf die Worker. Im Gegensatz zur oben vorgestellten parallelen Lösung, bei der die Arbeitsprozesse alle Argumente beim Start erhalten, muss der Verteiler nun mit den Workern auch nach ihrem Start kommunizieren.

Als Kommunikationskanäle fungieren so genannte Named Pipes oder Fifos. Der Dispatcher unterhält eine Pipe je Worker, in die er die neuen Aufträge schreibt. Eine weitere, dem Dispatcher und den Workern gemeinsame Pipe dient als Rückkanal. Wenn ein Worker nichts zu tun hat, schreibt er seine ID in die Pipe. Der Dispatcher liest nach jedem Auftrag aus der Pipe eine ID aus und schickt den nächsten Auftrag an diesen Worker.

In Listing 4 ist eine solche Implementation abgedruckt. In den Zeilen 1 bis 4 setzt das Programm eine Reihe von Konstanten, falls noch nicht geschehen. Normalerweise definiert der Nutzer nur die Variable »_cmd«. Die Funktion »dispatchWork« in den Zeilen 54 bis 72 ist quasi der öffentliche Teil der Schnittstelle. Zuerst erzeugt die Funktion ein temporäres Verzeichnis für alle Pipes in Zeile 55 (im Skript »controlDir« genannt). Der Befehl »mkfifo« in Zeile 58 legt den Rückkanal an.

Listing 4: Dynamischer
Dispatcher

001: ${DEBUG:=0}
002: ${_cmd:=echo}
003: ${PMAX:=`ls -1d /sys/devices/system/cpu/cpu* | wc -l`}
004: ${FDOFF:=4}
005:
006: processWorkItem() {
007:   eval $_cmd "$1"
008: }
009:
010: processWorkItems() {
011:   local line workerFifo="$1" dispatcherFifo="$2" id="$3" fd
012:   exec 3<>"$dispatcherFifo"
013:   while ! echo "$id" >&3; do
014:     sleep 1
015:   done
016:   let fd=id+FDOFF
017:   while true; do
018:     read -r -u $fd line
019:     if [ $? -ne 0 ]; then
020:       break
021:     fi
022:     if [ "$line" = "EOF" ]; then
023:       break
024:     else
025:       processWorkItem "$line"
026:       while ! echo "$id" >&3; do
027:         sleep 1
028:       done
029:     fi
030:   done
031:   rm -f "$workerFifo"
032: }
033:
034: startWorker() {
035:   local i fd fifo
036:   for (( i=0; i<PMAX; ++i )); do
037:     workerFifo="$controlDir/worker-$i"
038:     mkfifo "$workerFifo"
039:     let fd=i+FDOFF
040:     eval exec $fd<> "$workerFifo"
041:     processWorkItems "$workerFifo" "$dispatcherFifo" "$i" &
042:   done
043: }
044:
045: stopWorker() {
046:   local i fifo
047:   for (( i=0; i<PMAX; ++i )); do
048:     fifo="$controlDir/worker-$i"
049:     echo "EOF" > "$fifo"
050:   done
051:   wait
052: }
053:
054: dispatchWork() {
055:   local idleId dispatcherFifo  controlDir=`mktemp -d`
056:
057:   dispatcherFifo="$controlDir/dispatcher"
058:   mkfifo "$dispatcherFifo"
059:   exec 3<>"$dispatcherFifo"
060:
061:   startWorker
062:
063:   while read -r -u 0 line; do
064:    read -u 3 idleId
065:    echo "$line" >>   "$controlDir/worker-$idleId"
066:   done
067:
068:   stopWorker
069:
070:   rm -f "$dispatcherFifo"
071:   rm -fr "$controlDir"
072: }

Erklärungsbedürftig ist die Zeile 59. Hier öffnet die Shell den Rückkanal lesend und schreibend, obwohl eigentlich nur ein lesender Zugriff notwendig ist. Das Problem ist, dass bei einem nur lesendem Zugriff auf eine Pipe der Systemcall blockiert. Ein ganz ähnliches Problem ergibt sich auch in der Funktion »startWorker()«. Sie erzeugt für jeden Worker-Prozess eine Pipe (ab Zeile 37) und öffnet sie wieder sowohl lesend als auch schreibend (Zeile 40).

Das zusätzliche »eval« in dieser Zeile ist notwendig, da der Bash-Parser die Eingabeumleitung sehr früh - noch vor der Variablensubstitution - bearbeitet. Deshalb sind auch die Backslashes vor dem Kleiner- und Größerzeichen notwendig. Der Rest sollte selbsterklärend sein.

Listing 4 besteht nur aus Funktionen - andere Skripte inkludieren diese Datei und nutzen dann die Funktion »dispatchWork«. Das Listing 1 sieht damit wie Listing 5 aus.

Listing 5: Dispatcher im
Einsatz

001 source workDispatcher
002 doDynamic() { _cmd="doSomething" local item for item in "$@"; do echo "$item" done | dispatchWork }
Sie können diesen Artikel als PDF für 99 Cent kaufen. Klicken Sie dazu einfach auf eine der beiden Bezahloptionen Paypal oder ClickandBuy.


Diesen Artikel druckenDiesen Artikel weiterempfehlen Diesen Artikel kommentieren Newsletter abonnieren
Share/Bookmark
Ähnliche Artikel
Vom Thron gestürzt Die besten Lesereinsendungen des Programmierwettbewerbs
Aufpoliert Neue Funktionen in der Bash-Version 4
Wissenstransfer Aussichtsreiche Würfelkandidaten unter der Lupe
Ready, Set, Go! Google erfindet neue Programmiersprache
Zauberlehrling Automatische Bildbearbeitung mit Imagemagick
Tooltipps Werkzeuge im Kurztest
Whitepaper
The Role of Open Source in Data Integration

Obwohl in den letzten Jahren viele technische Fortschritte erzielt werden konnten, verfügen die meisten Datenintegrationsprozesse nach wie vor nur über eine sehr begrenzte Automatisierung. Das vorliegende White Paper von dem Industry Analyst Mark Madson wird zunächst ein grundlegendes Verständnis von Daten Integration vermitteln, die Vorzüge von Open Source Lösungen für Daten Integration erläutern und Ihnen professionelle Empfehlungen geben, damit Sie Ihre Integrationsjobs noch einfacher und produktiver gestalten können.

Download PDF (Registrierung erforderlich)
Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele (Folge 2)

Der zweite Teil des Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele White Papers beleuchtet anhand weiterer ausgewählter Case Studies die Implementierung von Open Source Datenintegration in der Praxis und benennt die daraus resultierenden Vorteile.

Download PDF (Registrierung erforderlich)
Kommentare (0)