Signalisierung aller Threads in einem Prozess

9

Ohne eine Liste der aktuellen Threads zu behalten, versuche ich zu sehen, dass ein Echtzeit-Signal an alle Threads in meinem Prozess geliefert wird. Meine Idee ist, so vorzugehen:

  • Zunächst wird der Signal-Handler installiert und das Signal wird in allen Threads entsperrt.
  • Wenn ein Thread das "Broadcast" -Signal senden möchte, erwirbt er einen Mutex und setzt ein globales Flag, dass die Übertragung stattfindet.
  • Der Sender blockiert das Signal (unter Verwendung von pthread_sigmask ) für sich selbst und tritt wiederholt in eine Schleife ein, die raise(sig) aufruft, bis sigpending anzeigt, dass das Signal ansteht (es waren keine Threads mehr vorhanden, bei denen das Signal blockiert war).
  • Wenn Threads das Signal empfangen, handeln sie darauf, warten aber im Signalhandler darauf, dass das Broadcast-Flag gelöscht wird, so dass das Signal maskiert bleibt.
  • Der Absender beendet die Schleife, indem er das Signal entsperrt (um seine eigene Zustellung zu erhalten).
  • Wenn der Absender sein eigenes Signal verarbeitet, löscht er das globale Flag, so dass alle anderen Threads mit ihrem Geschäft fortfahren können.

Das Problem, auf das ich stoße, ist, dass pthread_sigmask nicht respektiert wird. Alles funktioniert gut, wenn ich das Testprogramm unter strace starte (vermutlich aufgrund eines anderen Zeitplans), aber sobald ich es alleine laufen lasse, erhält der Sender sein eigenes Signal (obwohl er es blockiert hat ..?) Und keines der andere Threads werden jemals geplant.

Irgendwelche Ideen, was könnte falsch sein? Ich habe versucht, sigqueue anstelle von raise zu verwenden, die Signalmaske zu untersuchen und sleep überall hinzuzufügen, um sicherzustellen, dass die Threads geduldig auf ihre Signale usw. warten, und jetzt bin ich ratlos.

Bearbeiten: Dank der Antwort von psmears glaube ich, das Problem zu verstehen. Hier ist eine mögliche Lösung. Feedback wäre großartig:

  • Zu jedem Zeitpunkt kann ich die Anzahl der laufenden Threads kennen, und ich kann alle Thread-Erstellung und das Beenden während des Broadcast-Signals verhindern, wenn es nötig ist.
  • Der Thread, der das Broadcast-Signal senden will, erwirbt eine Sperre (also kann kein anderer Thread dies gleichzeitig tun), blockiert dann das Signal für sich selbst und sendet num_threads -Signale an den Prozess und entsperrt dann das Signal für sich selbst.
  • Der Signalhandler inkrementiert einen Zähler atomar, und jede Instanz des Signalhandlers wartet, bis dieser Zähler gleich num_threads ist, um zurückzukehren.
  • Der Thread, der die Übertragung durchgeführt hat, wartet auch darauf, dass der Zähler num_threads erreicht, und gibt dann die Sperre frei.

Eine mögliche Sorge ist, dass die Signale nicht in die Warteschlange gestellt werden, wenn der Kernel nicht genügend Arbeitsspeicher hat (Linux scheint dieses Problem zu haben). Weißt du, ob sigqueue den Anrufer zuverlässig informiert, wenn er das Signal nicht in eine Warteschlange stellen kann (in diesem Fall würde ich eine Schleife machen, bis es erfolgreich ist), oder könnten Signale möglicherweise stillschweigend verloren gehen?

Bearbeiten 2: Es scheint jetzt zu funktionieren. Gemäß der Dokumentation für sigqueue wird EAGAIN zurückgegeben, wenn das Signal nicht in die Warteschlange gestellt wird. Aber aus Gründen der Robustheit habe ich beschlossen, sigqueue immer wieder aufzurufen, bis num_threads-1 signal handlers läuft, und Aufrufe nach sched_yield verschachtelt, nachdem ich num_threads-1 signals gesendet habe.

Zum Zeitpunkt der Thread-Erstellung gab es eine Racebedingung, die neue Threads zählt, aber ich habe sie mit einer seltsamen (ab) Verwendung von Lese-Schreib-Sperren gelöst. Die Thread-Erstellung ist "Lesen" und das Broadcast-Signal ist "Schreiben". Wenn also kein Thread versucht zu senden, wird kein Konflikt bei der Thread-Erstellung erzeugt.

    
R.. 17.11.2010, 14:36
quelle

4 Antworten

4

raise() sendet das Signal (nur) an den aktuellen Thread, so dass andere Threads es nicht empfangen. Ich vermute, dass die Tatsache, dass strace die Dinge zum Laufen bringt, ein Fehler in strace ist (aufgrund der Art und Weise, wie es funktioniert, fängt es alle Signale ab, die an den Prozess gesendet werden, und erhöht sie dann wieder auf die falsche Art ...).

Sie können wahrscheinlich mit kill(getpid(), <signal>) umgehen, um das Signal als Ganzes an den aktuellen Prozess zu senden.

Ein anderes potentielles Problem, das Sie möglicherweise sehen, ist, dass sigpending() anzeigen kann, dass das Signal für den Prozess aussteht, bevor alle Threads es empfangen haben - das bedeutet, dass mindestens ein solches Signal für den Prozess ansteht. und es ist noch keine CPU verfügbar, um einen Thread zum Ausliefern auszuführen ...

Können Sie näher beschreiben, was Sie erreichen möchten? Und wie tragbar soll es sein? Es gibt fast sicher einen besseren Weg, es zu tun (Signale sind fast immer ein großer Kopfschmerz, besonders wenn sie mit Fäden gemischt werden ...)

    
psmears 17.11.2010, 15:17
quelle
1

In Multithread-Programmen ist raise (sig) äquivalent zu pthread_kill (pthread_self (), sig). Versuchen Sie zu töten (getpid (), sig)

    
blaze 17.11.2010 15:05
quelle
0

Vorausgesetzt, dass Sie die Thread-Erstellung und -Zerstörung scheinbar sperren können, könnte der Thread "broadcasting" nicht nur die erforderlichen Aktualisierungen für Thread-local-state in einer Thread-Warteschlange bereitstellen, die jeder Thread überprüft, wenn er verwendet wird der Thread-Local-State? Wenn es noch Updates gibt, wendet es sie zuerst an.

    
caf 18.11.2010 03:53
quelle
0

Sie versuchen, eine Reihe von Threads zu synchronisieren. Aus Sicht des Entwurfsmusters wäre die native pthread-Lösung für Ihr Problem eine Pthread-Barriere.

    
spfendtner 09.02.2015 19:56
quelle