Multithread lock read/write



  • intern im microcode ist es doch ein load und ein store Befehl die soweit ich weiss unterbrechbar sind - also ist eine bei-byte-ist-es-sicher-Aussage sehr eng begrenzt, da ist man doch schon nah an der atomaren int oder volatile mystik drann



  • Wenn du ein load und store hast (z.b. zahl++), dann geb ich dir recht. das gibt Probleme. Aber die anwendung sieht für mich nach Store-only aus (also der Schreibthread).



  • Gast3 schrieb:

    Nochmal kurz:
    Es soll von den ankommenden Daten keine "übersehen" werden.
    Es muss nicht jede angekommende Nachricht ausgegeben werden (z.b nur der aktuell gespeicherte Wert bei der Abfrage alle 1000ms)

    -> Grund: Sollte ein Wert einmalig kommen und ich übersehe ihn, dann sieht es so aus als ob nichts angekommen wäre.

    Erklär doch mal ganz genau was es für Daten sind - was du damit machst (über die Zeit - filtern, summieren) und wie diese angezeigt werden sollen (der "letzte" Wert selbst oder der Durchschnitt oder was)

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include <gtk/gtk.h> 
    #include <pthread.h> 
    
    typedef struct{ 
    	double wert; 
    	char einheit; 
    } sarray; 
    
    typedef struct _workfields{ 
    sarray arr1[5]; 
    
    } WorkFields; 
    
    void readData(WorkFields *work){ //hiervon Thread erzeugen 
    	//Hardware Initialisierungen... 
    	int i; 
    	uint64_t verarbeiten; 
    	double ergebnis;
    
    	while(1){ 
    	//aus Schnittstelle Daten empfangen und ins Struct *work schreiben
    
    		for(i=0; i<5; i++){
    			//Jedes "i" ist an einen eigenen Sensor gekoppelt
    			verarbeiten = empfangenvonSensor(i);  
    			//verarbeiten: Bits schieben, Multiplikation, Addition, Vergleiche
    
    			ergebnis = ...;
    			//In Struct schreiben
    			work->arr1[i].wert = ergebnis;
    		}
    	} 
    } 
    
    void printData(WorkFields *work){ 
    	//Ausgabe in GUI
    	int i;
    	char str[100];
    
    	while(1){
    		for(i=0; i<5; i++){
    			sprintf(str, "%f %s", work->arr1[i].wert, work->arr1[i].einheit); //Muss so sein, sonst kann ich es nicht in der GUI ausgeben
    			printf("%s\n", str); //Nur als Beispiel, eig. ohne printf in GUI ausgeben
    		}
    		usleep(500);
    	}
    } 
    
    int main(int argc, char *argv[]){ 
    
    	WorkFields *work; 
    	work = g_slice_new(WorkFields); 
    
    	//... weitere Deklarationen
    
    	pthread_t thread1, thread2; 
    
    	pthread_create (&thread1, NULL, readData, work);
    	pthread_create (&thread2, NULL, printData, work); 
    
    	//... GUI erstellen
    
    	gtk_main (); 
    	gdk_threads_leave (); 
    
    	return 0; 
    }
    

    So könnte das Programm in etwa aussehen. Ausgegeben wird nur der aktuell gespeicherte Wert, kein Durchschnittswert o.ä.

    Lg



  • Ok, das dürfte zu komplex sein.
    Dann hätte ich hier mal einen Vorschlag (Stichwort: nichtblockierende Synchronisation). Aber nicht blind übernehmen. Es sollte nochmal jemand drüberschauen, ob es nicht doch einen Fall gibt, der es kaputt macht.

    Die Idee ist:
    Der Schreibthread schreibt immer.
    Der Lesethread liest, wenn er denk, der Zeitpunkt ist gut. Wenn während des Lesens aber neue werte geschrieben wurde, wird das Ergebnis einfach verworfen und später erneut versucht.

    // Globale Variablen (volatile, damit garantiert richtiger wert gelesen wird)
    volatile bool flag_Running; // gibt an, ob Schreibthread gerade schreibt
    volatile bool flag_Dirty; // Gibt an, ob werte schon gelsen wurden
    
    // Thread Schreiben:
    while(1) {
        flag_Running = true;
        flag_Dirty = true;
            // Der normale Schreibkram
            // Eventuell sollte die Berechnung und alles vorher gemacht werden, um die Schreibzeit zu minimieren
        flag_Running = false;
    }
    
    // Thread Lesen:
    while(1) {
        do {
            // wir warten bis gerade nicht geschrieben wird
            while(flag_Running) { sleep(10); }
            // danach erkennen wir wieder neue Daten
            flag_Dirty = false;
            // verdammt, es wird schon wieder geschrieben. Dann doch nochmal warten
            if (flag_Running) continue;
                // jetzt koenen wir endlich lesen.
                // erstmal alles schnell in  lokale variablen lesen. danach den sprintf kram machen
        // falls Dirty gesetzt wurde waehrend des lesen, haben wir Muell gelesen eventuell. Dann nochmal das ganze von vorne
        } while (flag_Dirty);
        // Uns ist es gelungen alles zu lesen
        printf(...);
        sleep(1000);
    }
    

    Das ganze klappt aber nur, wenn deine Timings passen. D.h. der Schreibthread muss lange genug außerhalb des flag_Running==true Bereichs sein. Und Das Lesen muss zügig gehen. D.h. z.B. in 99% der Fälle schafft es der Lesethread die Daten zu Lesen, bevor die neuen Daten vorbereitet wurden. Und in dem restlichen 1% muss er es halt onchmal versuchen.



  • // Globale Variablen (volatile, damit garantiert richtiger wert gelesen wird)
    volatile bool flag_Running; // gibt an, ob Schreibthread gerade schreibt
    volatile bool flag_Dirty; // Gibt an, ob werte schon gelsen wurden

    das ist nicht threadsicher und dein volatile verhindert nur eine moegliche wegoptimierung (da der kompiler denken koennte die Thread-proc werden nicht genutzt) - oder was meinst du mit "volatile, damit garantiert richtiger wert gelesen wird"? soviel zur volatile Mystik



  • In welchem Fall würde es denn kaputt gehen? Kannst du einen konkreten Ablauf angeben?


  • Mod

    DerBaer2 schrieb:

    In welchem Fall würde es denn kaputt gehen? Kannst du einen konkreten Ablauf angeben?

    In den gleichen Fällen, wie wenn dort kein volatile stünde. volatile hat mit Threadsicherheit einfach überhaupt nichts zu tun. Sonst wäre es schließlich nicht nötig gewesen, für C11 richtige memory barriers, atmoics und vieles mehr einzuführen.



  • die volatile Lüge wird seit Jahren gerne und unwissend kopiert und dann auch noch weiter gelehrt - volatile sichert die Variable nur gegen Wegptimierung ab - sonst gar nichts - keine Magie und Mystik in Kombination mit Threads



  • Was ist volatile genau?
    Von der Wortherkunft her bedeutet es "veränderlich", "flüchtig". Non-volatile (also das, womit du am Häufigsten arbeitest) ist demnach nicht veränderlich.

    Im Kontext der Programmierung bedeutet dies: eine volatile Variable kann sich jederzeit ändern. Einfach so. Von einem Cycle auf den anderen. Auch, wenn der Compiler nicht einmal einen Zugriff auf die Variable im Code sieht - gerade war x noch 1, jetzt ist es 2, und dazwischen hat niemand eine Zuweisung gesehen.

    Natürlich ist eine Zuweisung/Veränderung erfolgt, aber sie ist für den Compiler nicht sichtbar. Und deswegen muss der Compiler jedes Mal die Variable vom Speicher holen, wenn auf sie zugegriffen wird. Vom lahmen Hauptspeicher jetzt. Der RAM. Früher war das der lange, weite Weg über die Northbridge. Und Optimizer haben versucht, diesen langen weiten Weg zu ersparen, indem sie oft verwendete Werte in den Registern gelassen haben. Und das geht ja so lange in Ordnung, wie der Wert in der Speicherstelle mit dem Wert im Register kongruent ist.

    Aber wenn jetzt, sagen wir mal, von irgendwo her ein Signalhandler/Interruipt, der einfach mal alles pausiert, aufgerufen wird, und x auf 2 ändert, und dein Programm dann weiter ausgeführt wird, dann ist der Wert im Register fehlerhaft. Weil im Speicher ja jetzt 2 steht. Das weiß der Code aber nicht, deswegen lädt er nicht nach.

    Deswegen kannst du mit volatile sagen, dass der Compiler die Variable nicht in ein Register laden soll. Sondern immer schön direkt aus dem Speicher. Das ist natürlich langsamer als ein Register. Aber besser als abrauchender Code.

    Jetzt habe ich allerdings auch eine Frage: volatile verhindert nicht, dass eine Variable in den CPU-Cache geladen wird - nur in ein Register oder eine "schönere Speicherstelle", korrekt? Damit wären volatile Variablen ein wenig langsamer als vollständig durchoptimierte Variablen, aber der Effekt wird doch drastisch abgemildert.



  • Danke für die zahlreichen Antworten.

    Ich weis jetzt leider noch nicht ganz, ob ich einen Mutex/anderen Lock benötige oder nicht.

    Ich möchte noch ergänzen das ich herausgefunden habe, das meine Empfangshardware einen eigenen Puffer verbaut hat.
    Somit ist es vielleicht doch ok, wenn er kurz auf den Mutex unlock warten müsste.

    //Initialisierung
    
    while(1){
    
       sleep(3);
       //während dem Wartezeitpunkt Sensorwerte erhalten
    
       wert = empfangewert();
    
    //...
    }
    

    Ich habe mal testweise während dem Sleep, also noch bevor ich beim schritt "wert = empfangewert();" gewesen bin, 5 Temperaturwerte manuell direkt hintereinander geschickt (kann ich machen). Die Werte kamen dann alle nacheinander im 3 sek Abstand an. --> Somit mussten die irgendwo Hardwareseitig zwischengespeichert gewesen sein.

    Des Weiteren kann ich angeben, das er z.B. den letzten Wert den er innerhalb von 500ms empfangen hat, weiter senden soll. Also muss er ja auch hier die empfangenen Werte puffern um den letzten weiter geben zu können.

    --

    Mein Programm funktioniert halt auch ohne einen Mutex. Solange es dadurch nicht zu abstürzen kommt und kein Wert verschwindet wäre alles ok.

    Benötige ich also einen Lock...?

    Gruß

    edit: Möchte noch eine andere Frage zum Verständnis loswerden:
    Mal angenommen in meinem Struct, wie beschrieben, wird auf die Variable "wert" nur von Thread1 zugegriffen (read/write) und die 2. Variable "einheit" wird nur von Thread2 verwendet (read/write). Dann bräuchte man keinen Lock, da es ja unterschiedliche Variablen sind. Oder stimmt das nicht, da sie sich im selben Struct befinden?



  • Ich möcht' dich eigentlich schlagen, weißt du das? Weil das alles nicht im OP steht und ich jetzt mühselig die einzelnen relevanten Informationen rauspicken muss.

    Wo steht im OP, dass die Werte von Hardware kommen? Steht da nicht.
    Was sind das für Sensoren, der man Werte schickt? Hast du selbst gesagt, dass du dir 5 mal hintereinander "geschickt hättest". Hat die Hardware einen Eingang, über den du den jetzigen Wert überschreiben kannst bzw. pushen? Wenn nicht, was für Werte kamen nach 3 Sekunden an?

    Das ist alles sehr unausgegoren und zeigt, dass du nicht wirklich verstehst, was du da machst.

    Wenn ich mit so einer Aufgabe betreut worden wäre, wäre mein Ansatz erst mal der folgende:

    - muss ich beim meinem Eingang (also dem Ausgang der Hardware) pollen? Pollen ist immer blöd. Gibt einen Grund, warum man bei Interrupts nicht pollt, sondern die IRQs den (A)PIC verarbeiten lässt.
    - wenn ich Ereignissteuerung machen kann, super! Lege ich fest, dass ich bei Nachrichteneingang aufgeweckt werden will, und lass den Prozess dann schlafen. Und wenn was reinkommt, verarbeite ich das. Das baue ich in einem Thread - ach, das baue ich dir direkt in main .
    - wenn ich pollen muss, Scheiße. Schaue ich nach, gibt es irgendwo Dokumentation dazu, ob die Harware mir eine Garantie macht, in welchem Intervall ein Update erfolgt. Mit einem Intervall von 1 ms kann man bereits ordentlich arbeiten, das Holen und Verarbeiten des Wertes wird weniger als 1 ms dauern. Ausgabe hängt von der Implementierung ab und kann länger als 1 ms dauern, aber dann ist das nicht mehr dein Fehler. Aber ständig und immer zu pollen, ergibt wenig Sinn. Zumal ich auch denke, dass die Hardware gar nicht so schnell ist.

    Was das ganze komplizierter macht, ist, dass du GTK verwendest. Fensteranwendungen sind ein wenig komplexer und müssen ständig reagieren. In beiden Fällen (Pollen und Nicht-pollen) brauchen wir dann zwei Threads. Der erste, der Hauptthread, ist mit dem Fenster beschäftigt, für den macht man nicht mal manuell einen Thread auf. Der zweite Thread pollt auf die Werte oder wird über neue Werte in Kenntnis gesetzt, und wenn es ein Update gab, lockt sich der zweite Thread den Mutex auf das GTK-Fenster ("gtk multithreading", einfach mal googlen), schreibt die Werte, und gibt den Mutex dann wieder frei.

    Den Mutex brauchst du hier übrigens, weil das Fenster eine gemeinsam genutzte Ressource ist. Da hilft kein Wenn und Aber - wenn's bei dir funktioniert, dann lass einfach mal laufen und wundere dich nicht, wenn das Fenster plötzlich einfriert und keine Updates mehr raushaut. Das passiert, weil das GTK-Framework mit seinem Status durcheinanderkommt.

    Also: der zweite Thread kümmert sich direkt um Einlesen und Schreiben. Der erste kümmert sich darum, dass das GTK-Fenster ordentlich angezeigt wird. Zwei Threads, zum Lesen und zum Schreiben, das würde ich ganz sein lassen. Dann hast du nämlich drei Threads, die untereinander synchronisiert werden müssen, und das ist noch schöner.
    Und wenn du gar kein GTK hast, dann brauchst du nicht mal Threads zu verwenden. Dann geht das alles in einem Thread.

    ABER das ist MEINE Implementierung - weil wir halt nicht wissen, wie das genau aussieht und du auch keine leicht zu erfassenden Informationen anbietest.

    JohnDillinger schrieb:

    Mal angenommen in meinem Struct, wie beschrieben, wird auf die Variable "wert" nur von Thread1 zugegriffen (read/write) und die 2. Variable "einheit" wird nur von Thread2 verwendet (read/write). Dann bräuchte man keinen Lock, da es ja unterschiedliche Variablen sind. Oder stimmt das nicht, da sie sich im selben Struct befinden?

    Undefiniert, Es kann sein, dass du auf einer CPU arbeitest, die nur, sagen wir mal, 8-Bytes auf einmal speichern und laden kann. Und wenn du zwei 32-Bit-ints direkt nebeneinander im Struct hast, wird der Compiler keine Padding-Bytes einfügen.
    Thread 1 will jetzt Wert 1 updaten, aber da die CPU nur 8 Byte atomar speichern kann, lädt diese halt Wert 2, generiert einen Wert, der beim Schreiben Wert 1 überschreibt, Wert 2 "praktisch" auch, aber da der Wert in der Theorie des Compilers der gleiche ist, merkst du das ja nicht (haha), und fertig ist der Lack.

    Problem ist, dass das nicht atomar ist. Wenn Thread 2 jetzt Wert 2 überschreibt, während Thread 1 noch am Werkeln ist, überschreibt Thread 1 am Ende noch das Update von Thread 2. Deswegen sind atomare Anweisungen bei asynchroner Ausführung ja so wichtig.



  • Ähem, dann wäre ich von volatile richtiggehend enttäuscht.
    Oder andersrum gefragt, was hätte volatile für einen Sinn, wenn es nicht voll auf die Targetadresse durchgreift?
    Denn ein anderer Thread kann ja auch nur auf die bekannte Adresse schreiben, woher soll der wissen, daß der andere nur im Cache rumnudelt?

    CPUs sind keine Autisten.
    volatile heißt, guck' nochmal nach, auch wenn der Code sagt, daß man da vorher 2 reingetan hat, schau' nach, ob wirklich 2 drinsteht.
    Und natürlich darf dann das der Compiler nicht wegoptimieren, den Rest macht der Memorycontroller, der bei Inkonsistenz nachlädt.

    Zumindest hab' ich das so verstanden, so genutzt und es hat so funktioniert. 🙂



  • Ja klar heisst volatile "durch bis zum Metall".
    Was volatile nicht heisst/macht:
    * atomic
    * memory ordering



  • olatile heißt, guck' nochmal nach, auch wenn der Code sagt, daß man da vorher 2 reingetan hat, schau' nach, ob wirklich 2 drinsteht.
    Und natürlich darf dann das der Compiler nicht wegoptimieren, den Rest macht der Memorycontroller, der bei Inkonsistenz nachlädt.

    Zumindest hab' ich das so verstanden, so genutzt und es hat so funktioniert.

    stimmt ja auch - nur das eben dadurch trotzdem keine Threadsicherer Zugriff realisiert ist, erstmal nur das überhaupt der Inhalt erreichbar ist - aber nicht ob der auch "im" richtigen Zustand ist



  • und darum schreibe ich auch Lüge oder Mystik - viele missbrauchen volatile als - Threadzugriffssicherung - obwohl es keine ist, es läuft durch pures Glück trotzdem - ist aber einfach total falsch



  • dachschaden schrieb:

    ...

    Hallo,

    sorry das ich mich nicht mehr gemeldet habe.

    Ich habe mir deinen Ansatz nochmal durch den Kopf gehen lassen und ein wenig gegoogled und nun eine stabile Lösung gefunden.

    Ich habe den Thread der mir in der GUI die Werte aktualisiert hat rausgeworfen und lasse das über den Haupthread (=GTK Thread) laufen. Dieser Hauptthread ruft mittels einer Timeout Funktion alle (z.B.) 500ms eine Funktion auf und diese verändert dann die GUI.

    Ein Sensor ist für mich eine Hardware. Jedenfalls läuft dieser wie gehabt in einem eigenen Thread. Beziehungsweise musste ich für jeden Sensor einen eigenen machen, da ich herausgefunden habe, dass das Programm stoppt bis ein neuer Wert angekommen ist. Was zur Folge hätte, das die anderen Sensoren in dieser Wartezeit nichts mehr empfangen hätten.

    Jedenfalls habe ich dann noch meinen Code optimiert, sodass pro Thread nur noch ein einziger Lese/Schreibzugriff auf das Struct nötig ist. Diese Zugriffe habe ich mittels eines Mutex jeweils gelockt.

    Habe mit dieser Lösung keinen speziellen "GTK-Lock" gebraucht, da nur der Hauptthread die GUI updatet.

    Es funktioniert perfekt!

    Danke 🙂



  • JohnDillinger schrieb:

    Es funktioniert perfekt!

    Danke 🙂

    Ich vermute mal, alle Beteiligten hier atmen jetzt tief durch und hoffen dass Dein Temperaturprogramm nicht "systemrelevant" ist - etwa zur Steuerung eines Atomkraftwerks...
    😃



  • JohnDillinger schrieb:

    Ich habe den Thread der mir in der GUI die Werte aktualisiert hat rausgeworfen und lasse das über den Haupthread (=GTK Thread) laufen. Dieser Hauptthread ruft mittels einer Timeout Funktion alle (z.B.) 500ms eine Funktion auf und diese verändert dann die GUI.

    Du wirst es nicht glauben, auf diese Idee bin ich auch gekommen. Allerdings hat dieser Ansatz ein kleines Problem: was, wenn die Hardware hängt? Sprich, Verbindung bricht ab, oder Hardware geht kaputt, und es können keine weiteren Daten geholt werden.

    Ich habe bereits ein Monitoring-Programm geschrieben. Kommt mehr als häufig genug vor, dass eine Verbindung hängen bleibt, und dann geht gar nichts mehr bis zum TCP-Timeout. Und in der Zeit werden dann alle Server und Dienste grün angezeigt, bis es eventuell zu spät ist.

    Wenn in deiner GTK-Anwendung das Werteholen hängt, reagiert die ganze Anwendung nicht mehr. Deswegen lagert man so was in einen externen Thread. Und wenn man mit mehreren Werten arbeitet - nun, kein Problem, ein LWP zu erstellen ist meistens immer noch schneller, als das ganze seriell zu machen. Wie reden hier schließlich von I/O.

    Bei meinem Ansatz kann die Anwendung beispielsweise alle 10s sich selbst prüfen, ob der Thread die Daten ordentlich geschrieben hat. Wenn nicht, wird der Eintrag für den Sensor rot dargestellt.
    Was machst du bei deinem Ansatz?

    JohnDillinger schrieb:

    Ein Sensor ist für mich eine Hardware. Jedenfalls läuft dieser wie gehabt in einem eigenen Thread. Beziehungsweise musste ich für jeden Sensor einen eigenen machen, da ich herausgefunden habe, dass das Programm stoppt bis ein neuer Wert angekommen ist. Was zur Folge hätte, das die anderen Sensoren in dieser Wartezeit nichts mehr empfangen hätten.

    Also - du lässt einen GTK-Timer feuern, der dann, sagen wir mal, 15 Threads erstellt, Werte aus den Sensoren lädt, Fenster aktualisiert, und dann wieder alle 15 Threads joined? Da stellen sich mir doch zwei Fragen:

    - In einem Timer hast du den GTK-Lock bereits, deswegen musst du das nicht selbst machen. Aber eventuell hast du immer noch das Problem, dass 15 Threads gleichzeitig den Status des Fensters ändern wollen. Fängst du das ab? Oder machst du es direkt intelligent und wartest, bis alles gejoint ist, und schreibst dann ordentlich die Werte in das GTK-Fenster?
    - Was ist, wenn ein Thread hängenbleibt, weil die Hardware hängenbliebt? Beim Joinen würde der Timerthread dann einfach ebenfalls hängen. Und die GTK-Anwendung hängt über allem.

    EDIT: @pointercrash():

    Hm. schau dir das mal an

    1. No, volatile doesn't guarantee no caching for this memory location, and there aren't anything about this in C/C++ Standards or compiler manual.
    2. Using memory mapped region, when memory mapped from device memory to CPU-memory is already marked as WC (write combining) instead of WB, that cancels the caching. And need not to do cache-flushing.

    Ist also nicht "runter bis zum Metall" in den meisten Fällen, zumindest verstehe ich das so. Und da, wo es wirklich runter bis zum Metall sein muss, übernimmt die CPU das schon selbst.
    Das scheint aber generell eine Wissenschaft für sich zu sein. Hier eine aufschlussreiche Artikelreihe von Ulrich Drepper.



  • Wenn in deiner GTK-Anwendung das Werteholen hängt, reagiert die ganze Anwendung nicht mehr. Deswegen lagert man so was in einen externen Thread.

    er hat doch einen Thread für das Sensorlesen der die Daten in den Struct schreibt - diese Daten holt er dann per GUI-Timer, so lese ich das



  • abcdefg schrieb:

    JohnDillinger schrieb:

    Es funktioniert perfekt!

    Danke 🙂

    Ich vermute mal, alle Beteiligten hier atmen jetzt tief durch und hoffen dass Dein Temperaturprogramm nicht "systemrelevant" ist - etwa zur Steuerung eines Atomkraftwerks...
    😃

    Isar 1 zurück ans Netz 🙂

    dachschaden schrieb:

    JohnDillinger schrieb:

    Ich habe den Thread der mir in der GUI die Werte aktualisiert hat rausgeworfen und lasse das über den Haupthread (=GTK Thread) laufen. Dieser Hauptthread ruft mittels einer Timeout Funktion alle (z.B.) 500ms eine Funktion auf und diese verändert dann die GUI.

    Du wirst es nicht glauben, auf diese Idee bin ich auch gekommen. Allerdings hat dieser Ansatz ein kleines Problem: was, wenn die Hardware hängt? Sprich, Verbindung bricht ab, oder Hardware geht kaputt, und es können keine weiteren Daten geholt werden.

    Ich habe bereits ein Monitoring-Programm geschrieben. Kommt mehr als häufig genug vor, dass eine Verbindung hängen bleibt, und dann geht gar nichts mehr bis zum TCP-Timeout. Und in der Zeit werden dann alle Server und Dienste grün angezeigt, bis es eventuell zu spät ist.

    Wenn in deiner GTK-Anwendung das Werteholen hängt, reagiert die ganze Anwendung nicht mehr. Deswegen lagert man so was in einen externen Thread. Und wenn man mit mehreren Werten arbeitet - nun, kein Problem, ein LWP zu erstellen ist meistens immer noch schneller, als das ganze seriell zu machen. Wie reden hier schließlich von I/O.

    Bei meinem Ansatz kann die Anwendung beispielsweise alle 10s sich selbst prüfen, ob der Thread die Daten ordentlich geschrieben hat. Wenn nicht, wird der Eintrag für den Sensor rot dargestellt.
    Was machst du bei deinem Ansatz?

    - Also wenn die Hardware hängt, bzw. kein neuer Wert innerhalb einer definierten Zeit reinkommt, bekomme ich das über eine Funktionsabfrage der Schnittstelle mitgeteilt. Das nutze ich auch aktiv, denn ich möchte ebenso dann etwas in rot anzeigen. Der Fall ist somit abgesichert.

    -LWP sagt mir nichts.

    - Wenn sich einer der Threads aufhängt, bekomme ich es nicht mit. Jetzt habe ich noch nicht verstanden, wie du diesen Fall absicherst?

    - Zum Aufhängen des Werteholen: Also wenn sich die Funktion die permanent mit timeout neu aufgerufen wird aufhängt, dann hängt sich alles auf. Aber warum sollte sich jetzt speziell diese Funktion aufhängen? "Weil es immer mal passieren kann", nehme ich an? Ich sags mal so, ist das die gängige Programmierweise, alles so zu 100% abzusichern? Also das Kernkraftwerk hänge ich jetzt nicht dran. Dann müsste ich ja in jeglichen anderen Programmen genauso vorgehen, und jede Funktion in ein thread auslagern, damit sich nicht alles aufhängt wenn sich eine Funktion verabschiedet. Also ich hoffe das ist verständlich was ich meine.
    Muss ich jede Funktion absichern? Es liegt ja nicht an dem Timeout, oder in Verbindung mit den anderen Threads, das diese Funktion besonders "absturzgefährdet" ist? Das soll nicht heissen, das ich den Vorschlag nicht annehmen möchte, das ist jetzt eine Interessensfrage.

    dachschaden schrieb:

    JohnDillinger schrieb:

    Ein Sensor ist für mich eine Hardware. Jedenfalls läuft dieser wie gehabt in einem eigenen Thread. Beziehungsweise musste ich für jeden Sensor einen eigenen machen, da ich herausgefunden habe, dass das Programm stoppt bis ein neuer Wert angekommen ist. Was zur Folge hätte, das die anderen Sensoren in dieser Wartezeit nichts mehr empfangen hätten.

    Also - du lässt einen GTK-Timer feuern, der dann, sagen wir mal, 15 Threads erstellt, Werte aus den Sensoren lädt, Fenster aktualisiert, und dann wieder alle 15 Threads joined? Da stellen sich mir doch zwei Fragen:

    - In einem Timer hast du den GTK-Lock bereits, deswegen musst du das nicht selbst machen. Aber eventuell hast du immer noch das Problem, dass 15 Threads gleichzeitig den Status des Fensters ändern wollen. Fängst du das ab? Oder machst du es direkt intelligent und wartest, bis alles gejoint ist, und schreibst dann ordentlich die Werte in das GTK-Fenster?
    - Was ist, wenn ein Thread hängenbleibt, weil die Hardware hängenbliebt? Beim Joinen würde der Timerthread dann einfach ebenfalls hängen. Und die GTK-Anwendung hängt über allem.

    der GTK Timer erstellt keine Threads.

    Ich erstelle die Threads in der main-Funktion, diese empfangen Daten und knallen sich mithilfe eines Pointers in ein Struct. Der Schreibebefehl wird jeweils durch einen Mutex Lock geschützt. Die Threads laufen permanent.

    Die Timeout-Funktion im Haupt(GTK-)thread holt sich eigenständig die Daten aus diesem Struct (geschützt durch einen Mutex) und nur diese Timeoutfunktion ändert den Status des Fensters. Die anderen Threads haben damit überhaupt nichts am Hut.

    Erübrigt sich damit deine Frage, was passiert wenn ein Thread hängen bleibt?
    Nebenbei wie oben bereits gefragt, wie möchtest du denn so ein Thread hängenbleiben absichern?


Anmelden zum Antworten