Perl: Script soll warten, wenn es bereits läuft

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
rannseier
Beiträge: 80
Registriert: 24.09.2007 12:37:30

Perl: Script soll warten, wenn es bereits läuft

Beitrag von rannseier » 20.01.2025 15:09:47

Hallo zusammen,

Ich habe hier ein altes Perlscript, dass im Grund nichts anderes macht, als sich einen freien Index aus einer Datenbank zu suchen, damit einen Datenbankeintrag erstellt und den Index zurückmeldet. Das funktioniert solange, wie das Script nicht zweimal parallel gestartet wird. Das zweite Script nimmt dann den gleichen freien Index und versucht damit erfolglos den Datenbankeintrag zu erstellen (Index schon belegt), so dass die darauf aufbauenenden Prozesse auf Fehler laufen, klassische Racecondition also.

Wie verhindere ich eine parallele Ausführung?

Ich habe überlegt, solange zu warten, bis das Script nur einmal als Prozess läuft. Wenn allerdings beide Script zeitgleich gestartet werden und dann gegenseitig warten, bis sie "fertig" werden, funktioniert das auch nicht.

Ideen dazu?

Benutzeravatar
heisenberg
Beiträge: 4132
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Perl: Script soll warten, wenn es bereits läuft

Beitrag von heisenberg » 20.01.2025 15:18:00

Parallele Ausführung verhindert man programmiersprachenübergreifend z. B. per Locking. Z. B. per File-Locking oder zu Deutsch Dateisperren. Es gibt aber noch andere Arten des Lockings.

Dateisperren kurz erklärt:

Du suchst dir einen Dateinamen aus. Z. B. /run/lock/deinprog/deinprog.lock. [1] Am Anfang des Programmes versucht, das Programm eine Sperre (=Lock) auf diese Datei zu erhalten. Eine Sperre kann - grob gesagt - immer nur ein Programm bekommen. Wenn das erhalten dieser Sperre fehlschlägt, beendet es sich wieder. Wenn es die Sperre erhält, dann führt sich das Programm aus und am Ende des Programmes wird die Sperre wieder freigegeben.

Bzgl. Locking gibt es noch zwei Varianten bzgl. der Art, wie die Sperre angefragt wird: Blockierend oder Nicht-Blockierend. Blockierend bedeutet, dass eine Sperre angefordert wird und dabei wird gewartet, bis die Sperre verfügbar ist. Nicht-Blockierend bedeutet, dass der Aufruf sofort beendet wird, entweder erfolglos ( Sperre nicht verfügbar) oder erfolgreich (Sperre erhalten).

Bei Perl gibt's hier u. a. die flock - Funktion:

https://perldoc.perl.org/functions/flock

Siehe auch:

https://de.wikipedia.org/wiki/Lock

[1] Der Filesystem Hierarchy Standard legt den Ablageort solcher Dateien auf den Pfad unterhalb von /run/lock fest.

juribel
Beiträge: 348
Registriert: 20.06.2023 10:17:01

Re: Perl: Script soll warten, wenn es bereits läuft

Beitrag von juribel » 20.01.2025 17:04:26

Wenn die simple und primitive Methode genügt: einfach im Skript beim Start eine Datei anlegen bzw. wenn die Datei existiert, das Skript mit einer Fehlermeldung beenden. Beim regulären Beenden des Skripts die Datei wieder entfernen. Wenn das Skript abgebrochen wurde, die Datei von Hand entfernen.

Benutzeravatar
GregorS
Beiträge: 3192
Registriert: 05.06.2008 09:36:37
Wohnort: Freiburg
Kontaktdaten:

Re: Perl: Script soll warten, wenn es bereits läuft

Beitrag von GregorS » 20.01.2025 18:10:09

Teste am Anfang des Scripts, ob eine „Lock-Datei“ vorhanden ist. Wenn die Datei vorhanden ist, beendest Du – wenn nicht, erstellst Du sie (z.B. touch).
Als Shellscript etwa so:

Code: Alles auswählen

gszaktilla@lili:~/test$ cat bla.sh 
#!/bin/sh

if [ -e /var/lock/bla-lock ]; then
  echo "Wird schon ausgefuehrt. Ende."
  exit
else
  touch /var/lock/bla-lock
  sleep 10 # oder tu sonstwas
  rm /var/lock/bla-lock
fi
gszaktilla@lili:~/test$ ./bla.sh &
[1] 19905
gszaktilla@lili:~/test$ ./bla.sh 
Wird schon ausgefuehrt. Ende.
gszaktilla@lili:~/test$ 
Wenn man keine Probleme hat, kann man sich welche machen. ("Großes Lötauge", Medizinmann der M3-Hopi [und sog. Maker])

juribel
Beiträge: 348
Registriert: 20.06.2023 10:17:01

Re: Perl: Script soll warten, wenn es bereits läuft

Beitrag von juribel » 20.01.2025 18:15:59

Bei Bedenken, was passieren könnte, wenn das Skript zweimal gleichzeitig gestartet wird und beide Instanzen noch keine Lock-Datei vorfinden, könnte man auch so vorgehen: die Datei erzeugen und seine eigene Prozess-ID hineinschreiben. Anschliessend wieder einlesen. Wenn die Prozess-ID die eigene ist, weitermachen, wenn nicht, mit einer Fehlermeldung abbrechen.

tobo
Beiträge: 2400
Registriert: 10.12.2008 10:51:41

Re: Perl: Script soll warten, wenn es bereits läuft

Beitrag von tobo » 20.01.2025 18:23:29

Als Ergänzung der Beträge darüber sollte man das Entfernen eines Locks in einem Trap-Pendant (zur Shell) ausführen, um auch auf Abbrüche reagieren (den Lock entfernen) zu können:
https://www.perl.com/article/37/2013/8/ ... s-in-Perl/

Benutzeravatar
heisenberg
Beiträge: 4132
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: Perl: Script soll warten, wenn es bereits läuft

Beitrag von heisenberg » 21.01.2025 16:03:49

Ich habe (auch) lange gedacht: Locking? Für das bisschen Shellscripting brauch ich das nicht! Ich mache mir das einfach und für das bisschen, was ich tue reicht das schon. Leider musste ich öfters erleben, wie mir Dinge Ärger gemacht haben, weil das dann doch gebrochen ist.

Ein simples Beispiel, dass die Methode "flock" und "lock per touch" testet. Das ist jetzt zur Darstellung übertrieben, aber nichts desto trotz wird es im laufenden Betrieb vermutlich irgendwann vorkommen und wenn es vorkommt, dann kotzt man im Strahl, weil man vermutlich mit einem Fehler konfrontiert ist, der vielleicht gar nicht mal so einfach zu finden ist. Also empfehle ich, dass lieber gleich sauber zu machen - vor allem, wenn da schon eine Datenbank im Spiel ist. Und vom Aufwand macht es auch keinen Unterschied. Man muss sich nur kurz die Zeit nehmen, um das einmal gemacht zu haben.

Es soll ein Script, dass parallel aufgerufen wird, 100 Mal ausgeführt werden, um in einer Datei einen Zähler um 1 zu erhöhen. Dabei muss sichergestellt sein, dass jede einzelne Erhöhung geschrieben wird. Der Zähler ist vorher 0 und sollte also danach 100 sein.

Ich habe 3 Bash-Scripte geschrieben:
  1. Ein Script, dass das mit "lock per touch" macht (NoPaste-Eintrag42304)
  2. Ein Script, dass das mit "flock" tut (NoPaste-Eintrag42303)
  3. Ein Testscript, dass mehrere Tests durchführt (NoPaste-Eintrag42302):
    1. 100 mal sequentiell ausführen für beide lock-scripte
    2. 100 mal parallel ausführen für beide lock-scripte
So ist das Ergebnis dann:

Code: Alles auswählen

$ ./test_program 

Data Value after 100 sequential executions with simple lock: 100
Data Value after 100 sequential executions with flock:       100

Data Value after 100 parallel executions with simple lock:     9
Data Value after 100 parallel executions with flock          100

$ ./test_program 

Data Value after 100 sequential executions with simple lock: 100
Data Value after 100 sequential executions with flock:       100

Data Value after 100 parallel executions with simple lock:    44
Data Value after 100 parallel executions with flock          100

$ ./test_program 

Data Value after 100 sequential executions with simple lock: 100
Data Value after 100 sequential executions with flock:       100

Data Value after 100 parallel executions with simple lock:     4
Data Value after 100 parallel executions with flock          100

$ ./test_program 

Data Value after 100 sequential executions with simple lock: 100
Data Value after 100 sequential executions with flock:       100

Data Value after 100 parallel executions with simple lock:     9
Data Value after 100 parallel executions with flock          100

$ ./test_program 

Data Value after 100 sequential executions with simple lock: 100
Data Value after 100 sequential executions with flock:       100

Data Value after 100 parallel executions with simple lock:     4
Data Value after 100 parallel executions with flock          100
Man sieht direkt, dass das einfache Lock mit touch für Parallelität absolut ungeeignet ist. Das ist im Endeffekt eher ein Zufallszahlengenerator als eine verlässliche Ausführungssteuerung. Die flock-Variante läuft in jedem Fall einwandfrei.

Nachdem im Vorlagescript "locking mit touch", das Locking als nicht-blockierende Variante war, habe ich das mit flock hier genauso gemacht. Damit es aber einfacher ist, würde ich hier `blockierend` bevorzugen.

Antworten