Ich habe ein C-Programm unter Linux laufen bei dem sich ein Heisenbug zeigt: Zuerst funktionierte damit die 4 Eingangs-Pins von einer seriellen Schnittstelle (Ri, ...) einzulesen, aber nach ein bischen Ausbauen zeigt sich ein Heisenbug, auch ohne Optimierung: Wird das Programm ohne #define DEBUG compiliert, werden keine Daten von dem Lese-Thread eingelesen, mit dem define werden sie korrekt eingelesen, obwohl das Define nur zusätzliche Ausgaben macht, wie der Name sagt zum Debuggen. Deshalb habe ich mit Intervallhalbierung die Zeile gefunden, die den Unterschied zwiscnen funktionieren/nicht funktionieren ausmacht und es ist die Zeile (void) fprintf (stderr, "main: Created thread number %d, for parallel reading from serial device %d.\n", gi_pt_number, i); Das heißt ist die Zeile auskommentiert funktioniert das Einlesen nicht, ist sie nicht auskommentiert, funktioniert es. Aber da wird nichts an der Programmausführung modifiziert, wie kann das sein dass es doch die Programmausführung massiv beeinflußt? Zum Kompilieren verwende ich: gcc -Wall -D_REENTRANT -lm -pthread -O0 -o logger logger.c -lrt
:
Bearbeitet durch User
Kannst du das Programm als Anhang hochladen? Erfahrungsgemäß würde ich nicht darauf vertrauen, dass das Problem in der genannten Zeile liegt. Oft wird der eigentliche Fehler nur verdeckt, wenn man eine Zeile auskommentiert, aber der Fehler liegt an einer ganz anderen Stelle. Bist du sicher, dass du in den Debuganweisungen nur Ausgaben machst und nicht aus Versehen irgendwas wie if(a=b) oder eine Funktion mit Seiteneffekten ausführst?
Sind alle funktionen, die du verwendest, eintrittsinvariant oder Thread-sicher? Warum überhaupt Threads?
mit fprintf schaffst du eine Synchronisierung zwischen den Threads weil immer nur ein Thread auf stderr schreiben kann. Das deutet darauf hin, das ein irgendwo nicht Thread Save bist. z.b. Globale Variablen
Nase schrieb: > Sind alle funktionen, die du verwendest, eintrittsinvariant oder > Thread-sicher? Ja, ich achte darauf indem die Threads eine eigene Aufgabe bekommen. > Warum überhaupt Threads? Ganz einfach: Für jede parallele Aufgab ein Thread.
Peter II schrieb: > mit fprintf schaffst du eine Synchronisierung zwischen den Threads weil > immer nur ein Thread auf stderr schreiben kann. > > Das deutet darauf hin, das ein irgendwo nicht Thread Save bist. z.b. > Globale Variablen Die betreffende Zeile wird nur beim Start eines Threads aktiv, einmal. Aber sie verhindert dauerhaft das ein Tread Daten einliest.
> Ja, ich achte darauf indem die Threads eine eigene Aufgabe bekommen.
∗hüstel∗ du weisst schon was thread-sicher ist, oder?
Erwin Meyer schrieb: >> Warum überhaupt Threads? > > Ganz einfach: Für jede parallele Aufgab ein Thread. Das ist keine Rechtfertigung für Threads, sondern eine Ausrede für misslungene Architektur... Erwin Meyer schrieb: > Ja, ich achte darauf indem die Threads eine eigene Aufgabe bekommen. Und das wiederum könnte eine gute Begründung dafür sein, dass du keine Threads nehmen solltest.
Inzwischen habe ich den Fehler gefunden: Das letzte Argument bei pthread_create war immer nur eine Variable, wie bei Call by value üblich, aber die Funktion macht Call by reference. Durch das Hochzählen dieser Variablen und verwenden für alle Posix-Threads stimmten die IDs der Threads unter Umständen nicht mehr. Mal funktioniert es, mal nicht. Mit einem Datenfeld mit index = Wert ist das Problem sauber gelöst.
Du solltest trotzdem Deine Methodik überdenken: Wenn alle Threads etwas anderes machen und sie überhaupt nicht miteinander interagieren, dann wären X Prozesse statt X Threads doch angebrachter. Jeder Prozess hat dann seinen eigenen Adressraum und alle können friedlich nebeineinander koexistieren.
Frank M. schrieb: > Wenn alle Threads etwas anderes machen und sie überhaupt nicht > miteinander interagieren, dann wären X Prozesse statt X Threads doch > angebrachter. Jeder Prozess hat dann seinen eigenen Adressraum und alle > können friedlich nebeineinander koexistieren. nein, warum sollte man diesen krampf machen? Das hat man doch nur als Zwischenlösung gemacht, als es in Linux noch keine Threads gab. Der Anwender möchte ein Programm starten und beenden, nicht die Prozesse durchsuchen ob es irgendwo doch noch Leichen gibt. Da kostet das auch mehr Ressourcen, weil das Programm dann mehrfach ein Speicher ist. (ok das macht nicht so viel aus). Und es wird bestimmt noch mehr Gemeinsamkeiten geben, und was es das logfile ist.
Erwin Meyer schrieb: > Inzwischen habe ich den Fehler gefunden: Das letzte Argument bei > pthread_create war immer nur eine Variable, wie bei Call by value > üblich, aber die Funktion macht Call by reference. Durch das Hochzählen > dieser Variablen und verwenden für alle Posix-Threads stimmten die IDs > der Threads unter Umständen nicht mehr. Mal funktioniert es, mal nicht. > Mit einem Datenfeld mit index = Wert ist das Problem sauber gelöst. In Amerika dürfen die Studenten an einer bestimmten Universität erst mit dem Prof. sprechen, wenn sie ihr Problem zuvor einem Teddy erzählt haben. In deinem Fall war der Teddy das Forum. ;-)
Peter II schrieb: > nein, warum sollte man diesen krampf machen? Das hat man doch nur als > Zwischenlösung gemacht, als es in Linux noch keine Threads gab. Komisch, wenn ich ein aktuelles Linux starte, gibt es da immer (viel!) mehr als einen Prozess, der läuft? Mit Deiner Argumentation müsste man ja alles (z.B. apache, postfix, bind usw.) in einen Prozess (nennen wir ihn init ;-) ) stecken und alle Dienste als Thread laufen lassen. Wenn ich Linux herunterfahren will, reicht es dann, init zu killen - nach Deiner Argumentation. Geil! ;-) Und warum betrachtest Du X Prozesse als Krampf? Zum Krampf wird es doch, wenn ich jeden Threads davor schützen muss, dass ein anderer Thread nicht gerade seine globalen Variablen zerhackstückt... > Da kostet das auch mehr Ressourcen, weil das Programm dann mehrfach ein > Speicher ist. (ok das macht nicht so viel aus). Unsinn. Stichwort ist dabei Copy on Write. Neue Pages werden nach dem fork/exec erst dann angefordert, wenn sie auch mit (unterschiedlichen) Daten gefüllt werden. > Und es wird bestimmt noch mehr Gemeinsamkeiten geben, und was es das > logfile ist. Sorry, das ist sehr wohl alles auch mit X Prozessen machbar und überhaupt kein Problem.
Erwin Meyer schrieb: > Inzwischen habe ich den Fehler gefunden: Das letzte Argument bei > pthread_create war immer nur eine Variable, wie bei Call by value > üblich, aber die Funktion macht Call by reference. Durch das Hochzählen > dieser Variablen und verwenden für alle Posix-Threads stimmten die IDs > der Threads unter Umständen nicht mehr. Mja, Peter II (Gast) schrieb auch schon: > Das deutet darauf hin, das ein irgendwo nicht Thread Save bist. z.b. > Globale Variablen (Auch wenn er Thread-Safe gemeint hat)
Frank M. schrieb: > Mit Deiner Argumentation müsste man > ja alles (z.B. apache, postfix, bind usw.) in einen Prozess (nennen wir > ihn init ;-) ) stecken und alle Dienste als Thread laufen lassen. Wenn > ich Linux herunterfahren will, reicht es dann, init zu killen - nach > Deiner Argumentation. Geil! ;-) nein, ich will aber das der Webserver was für ein eigenständiges Programm ist als ein Prozess läuft. > Und warum betrachtest Du X Prozesse als Krampf? Zum Krampf wird es doch, > wenn ich jeden Threads davor schützen muss, dass ein anderer Thread > nicht gerade seine globalen Variablen zerhackstückt... wenn man gleich sinnvoll programmiert, hat man fast keine globalen Variablen. > > Und es wird bestimmt noch mehr Gemeinsamkeiten geben, und was es das > > logfile ist. > Sorry, das ist sehr wohl alles auch mit X Prozessen machbar und > überhaupt kein Problem. klar ist das machbar, aber nur mit Krampf. On-the-Fly in ein neues Logfile schreiben geht schlecht, weil man denn erst jeden Prozess darüber informieren müsste das es jetzt ein neues Handle gibt. Oder ist bei dir das Logging auch gleich ein neue Prozess?
Peter II schrieb: > nein, ich will aber das der Webserver was für ein eigenständiges > Programm ist als ein Prozess läuft. Aha. Also was hast Du an meinem Satz oben "Wenn alle Threads etwas anderes machen ..." (auf den Du reagiertest) nicht verstanden? > klar ist das machbar, aber nur mit Krampf. On-the-Fly in ein neues > Logfile schreiben geht schlecht, weil man denn erst jeden Prozess > darüber informieren müsste das es jetzt ein neues Handle gibt. man syslog.
Frank M. schrieb: > Aha. Also was hast Du an meinem Satz oben > > "Wenn alle Threads etwas anderes machen ..." > > (auf den Du reagiertest) ein Thread lesen der Daten, ein Thread verarbeiten der Daten und ein Thread für die Ausgabe. alles 3 Threads machen was anderes, trotzdem würde ich nicht auf die Idee kommen sie als 3 Prozesse auszuführen. > man syslog. toll - das vermutlich nicht das was man unter logging eines Prozesses versteht. Apache, Mysql, Bind, squid nutzen es schon mal nicht für log - wird wohl einen Grund haben.
Peter II schrieb: > Frank M. schrieb: >> Aha. Also was hast Du an meinem Satz oben >> >> "Wenn alle Threads etwas anderes machen ..." >> >> (auf den Du reagiertest) > > ein Thread lesen der Daten, ein Thread verarbeiten der Daten und ein > Thread für die Ausgabe. > > alles 3 Threads machen was anderes, trotzdem würde ich nicht auf die > Idee kommen sie als 3 Prozesse auszuführen. Und vermutlich schleppst du mit drei Threads mehr Unwägbarkeiten in dein Programm, als es ein schlichter Zustandsautomat tun würde. Skaliert dein Problem gut mit Threads? Wenn nur ein Thread Daten lesen kann und auch nur ein Thread diese Daten verarbeitet, dann skaliert es überhaupt nicht. Dann braucht man auch keine Threads.
Nase schrieb: > Skaliert dein Problem gut mit Threads? Ja, mit 0 bis 21 Threads läuft es problemlos, obwohl meine Rechner nur 4 bis 8 Cores haben. > Wenn nur ein Thread Daten lesen kann und auch nur ein Thread diese > Daten verarbeitet, dann skaliert es überhaupt nicht. Doch gerade dann sollte man Threads einsetzen, genau einen pro Schnittstelle, mit einem Ringpuffer in den nur dieser Tread T hinein schreibt und aus dem X andere nur lesen (meist nur main, aber andere können auch Auslesen), wobei T den offizellen Index auf die neuesten Daten erst inkrementiert nachdem sie in den Ringpuffer geschrieben wurden, um Locks einzusparen. Beispielsweise Polling: Ein Thread k schicht als Timer-Thread x anderen ein Signal wenn es wieder Zeit ist zum Einlesen (sonst macht dieser Thread nichts/schlafen) und das Einlesen machen dann diese x aufgeweckten Threads parallel von x Schnittstellen, z. B. Parallelports. Für wenig Jitter sollte man wenig load und x < Core-Anzahl haben (und lowlatency- oder realtime-Kernel, hohe Priorität etc.). Mit in der Praxis um 100 µs Jitter mit einem Lowlatency-Kernel auf einem billigen PC reicht das schon für viele Zwecke.
Eric B. schrieb: > Mja, Peter II (Gast) schrieb auch schon: > >> Das deutet darauf hin, das ein irgendwo nicht Thread Save bist. z.b. >> Globale Variablen > > (Auch wenn er Thread-Safe gemeint hat) Damit lag er falsch, denn es war ein Laufzeiteffekt: Das printf bewirkte das main länger brauchte und in der Zeit hat der Thread den übergebenen Paramter einlesen und verarbeiten könen. Ohne das printf war main schneller und modifizierte den Parameter bevor der neue Thread den Parameter verarbeitet hat. Das hat also mit globalen Variablen nichts zu tun, sondern war schlicht Call bei value angewendet wo tatsächlich Call by reference gemacht wird. Da könnte der GCC eigentlich eine Warnung ausgeben weil hier eine Race Condition vorliegt, aber bisher macht er das nicht.
Frank M. schrieb: > Du solltest trotzdem Deine Methodik überdenken: > > Wenn alle Threads etwas anderes machen und sie überhaupt nicht > miteinander interagieren, Das ist ja nicht der Fall, sonst wäre es ja konsequenterweise nicht ein Programm. Beispielsweise tauschen die Threads Signale aus und schreiben eingelesene Daten in Ringpuffer, die (bisher nur) main auswertet/ausgibt. Es gibt da also mehrere Kopplungen.
Erwin Meyer schrieb: > Das heißt ist die Zeile auskommentiert funktioniert das Einlesen nicht, > ist sie nicht auskommentiert, funktioniert es. Das ist aber kein Heisen-Bug ...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.