Der Martin: Win32-API: CreateThread(), Threads koordinieren

Hallo Kameraden,

gerade komme ich mal wieder in den Genuss, mich in meinem alten Spezialgebiet Softwareentwicklung zu bewegen. Und bin auf ein Phänomen mit einer win32-Anwendung gestoßen.

Um eine zusätzliche Aktion nebenläufig zu realisieren (Kommunikation über eine Sellerie-Schnittstelle), ohne das Hauptprogramm zu beeinflussen, habe ich diese zusätzliche Aktion in einen eigenen Thread verlagert - also ein paar sequentielle Aufrufe von WriteFile() und ReadFile() auf ein Comm-Handle. Nun habe ich beim Testen festgestellt, dass beim Schließen des Programms hin und wieder zwar das Programmfenster verschwindet, der eigentliche Prozess aber als Zombie übrigbleibt. Im Taskmanager ist er dann noch zu sehen.

Aufgefallen ist das, weil das Programm sich dann beim Neustart beklagt, die COM-Schnittstelle könne nicht geöffnet werden, weil sie noch von einem anderen Prozess belegt ist. Das Debugging gestaltet sich ein bissl unbequem, da der zusätzliche Thread in einer DLL steckt, die "on demand" geladen wird.

Ist es in win32 tatsächlich Pflicht, das Beenden aller Threads zu überwachen und abzuwarten, bevor man das Hauptprogramm enden lässt? Ich hätte gedacht, wenn der Thread noch ein paar Sekunden läuft, dann verzögert sich halt auch das Beenden des Hauptprozesses. Aber anscheinend ist das nicht so.

Hat da jemand Erfahrungen?

Live long and pros healthy,
 Martin

--
Home is where my beer is.
  1. Hallo Martin,

    Sellerie-Schnittstelle

    Prust 😀🤣

    Ich kenne das nur aus .net. Da muss ich beim Starten eines Threads sagen, ob das ein Backgroundthread ist oder nicht, und wenn es keiner ist, läuft der Prozess weiter, bis alle Nonbackground-Threads beendet sind.

    Grundsätzlich kann dieses Verhalten ja Absicht sein, dass ein Prozess sein Hauptprogramm beendet und nur im Hintergrund noch herummacht.

    Ich hätte gedacht, wenn der Thread noch ein paar Sekunden läuft, dann verzögert sich halt auch das Beenden des Hauptprozesses.

    Ist das so? Endet der Thread? Oder ist er vielleicht noch aktiv und wartet auf irgendwas? Solange der auf weiteren Sellerie wartet, dürfte er nicht zu Ende sein.

    Ich habe hier mal nachgelesen. Da stehen einige Hinweise am Ende. Betrifft Dich davon irgendwas?

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      Sellerie-Schnittstelle

      Prust 😀🤣

      kanntest du das noch nicht?? Das spielen sie in Holland doch schon auf der Luftpumpe.

      Grundsätzlich kann dieses Verhalten ja Absicht sein, dass ein Prozess sein Hauptprogramm beendet und nur im Hintergrund noch herummacht.

      Ja, dafür kann ich mir auch sinnvolle Szenarien vorstellen. Aber das würde ich dann eher so lösen, dass der Hauptprozess noch kontrolliert in seiner Message-Loop rumrennt und nur sein Fenster schließt. Dann hätte er (vom Berechtigungsstatus abgesehen) den Status eines Services.

      Ich hätte gedacht, wenn der Thread noch ein paar Sekunden läuft, dann verzögert sich halt auch das Beenden des Hauptprozesses.

      Ist das so? Endet der Thread?

      Das sollte er zumindest. Wie gesagt, das läuft in einer "frei fliegenden" DLL und ist deshalb schwierig zu debuggen. Aber der Thread arbeitet nur eine endliche Queue von WriteFile() und ReadFile() ab, jedesmal mit Timeout, so dass diese Aufrufe nach ein paar 100ms zurückkehren, notfalls auch erfolglos.

      Oder ist er vielleicht noch aktiv und wartet auf irgendwas? Solange der auf weiteren Sellerie wartet, dürfte er nicht zu Ende sein.

      Das ist klar, aber das gibt die Programmlogik eigentlich nicht her.

      Ich habe hier mal nachgelesen. Da stehen einige Hinweise am Ende. Betrifft Dich davon irgendwas?

      Diesen Abschnitt der Winapi-Doku habe ich schon gelesen. Der einzige Satz, den ich im Hinblick auf dieses Problem für interessant halte, ist dieser:

      ExitProcess does not complete until there are no threads in their DLL initialization or detach routines.

      Das lese ich so, dass ExitProcess() implizit das Beenden von Threads abwartet, die in DllMain() gestartet wurden. Bei mir wird der Thread aber nicht schon beim Initialisieren der DLL (also nicht in DllMain()) gestartet, sondern erst bei Bedarf aus einer "gewöhnlichen" exportierten Funktion heraus.

      Anyway, ich habe das inzwischen umgebaut und warte beim Beenden des Programms nun explizit ab, bis der Thread sich ordnungsgemäß verabschiedet hat. Ich komme aber frühestens heute nachmittag wieder ans Live-System zum Testen.

      Live long and pros healthy,
       Martin

      --
      Home is where my beer is.
      1. Hallo nochmal,

        Anyway, ich habe das inzwischen umgebaut und warte beim Beenden des Programms nun explizit ab, bis der Thread sich ordnungsgemäß verabschiedet hat. Ich komme aber frühestens heute nachmittag wieder ans Live-System zum Testen.

        nun habe ich die so umgebaute DLL mitsamt ihrer Host-Applikation bestimmt 20mal oder noch öfter im Lauf des Nachmittags gestartet und wieder beendet - nicht nur um die Beenden-Problematik zu untersuchen, sondern auch, um viele unterschiedliche Einstellungen zu testen. Ein Zombie, wie ursprünglich beschrieben, ist nicht wieder aufgetaucht.

        Ich nehme also an, dass es tatsächlich so ist: Man darf sich anscheinend nicht einfach so davonstehlen, während der Hund (Thread) noch frei rumläuft.

        Trotzdem danke fürs Mitdenken.

        Live long and pros healthy,
         Martin

        --
        Home is where my beer is.
  2. Moin,

    Ist es in win32 tatsächlich Pflicht, das Beenden aller Threads zu überwachen und abzuwarten, bevor man das Hauptprogramm enden lässt? Ich hätte gedacht, wenn der Thread noch ein paar Sekunden läuft, dann verzögert sich halt auch das Beenden des Hauptprozesses. Aber anscheinend ist das nicht so.

    Schreibst du dein Programm in C++? Seit C++ 2011 gibt es eine Thread Library, die dir die Arbeit deutlich erleichtert.

    Viele Grüße
    Robert

    1. Hallo,

      Ist es in win32 tatsächlich Pflicht, das Beenden aller Threads zu überwachen und abzuwarten, bevor man das Hauptprogramm enden lässt?

      Schreibst du dein Programm in C++?

      nein, reines Plain Vanilla C.

      Seit C++ 2011 gibt es eine Thread Library, die dir die Arbeit deutlich erleichtert.

      Nach erleichtern sieht das nicht gerade aus. Mit meiner reinen Windows-API-Lösung brauche ich genau zwei Funktionen: CreateThread() und GetExitCodeThread() - wobei letztere mir vor allem verrät, ob der Thread noch läuft oder schon beendet ist.

      Live long and pros healthy,
       Martin

      --
      Home is where my beer is.
      1. Moin,

        Ist es in win32 tatsächlich Pflicht, das Beenden aller Threads zu überwachen und abzuwarten, bevor man das Hauptprogramm enden lässt?

        ich habe noch einmal kurz nachgedacht: Die Antwort lautet sowohl unter Windows als auch Linux/Unix Ja. Es ist ein Unterschied ob ich den Thread joine oder detache. Im zweiten Fall läuft der Thread unabhängig vom aufrufenden Thread.

        Seit C++ 2011 gibt es eine Thread Library, die dir die Arbeit deutlich erleichtert.

        Nach erleichtern sieht das nicht gerade aus. Mit meiner reinen Windows-API-Lösung brauche ich genau zwei Funktionen: CreateThread() und GetExitCodeThread() - wobei letztere mir vor allem verrät, ob der Thread noch läuft oder schon beendet ist.

        Gut, dafür bräuchte man in C++ wohl eine Statusvariable, wenn der Thread nicht gejoined werden kann. Aber das Prinzip ist ziemlich ähnlich. Interessanterweise scheint es in C++ keine andere Möglichkeit zu geben herauszufinden, ob der Thread läuft.

        Viele Grüße
        Robert

      2. Hallo Martin,

        CreateThread() und GetExitCodeThread()

        ich sehe auf der CreateThread Seite gerade noch was anderes:

        A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.

        Verwendest Du die CRT im Thread? Das ist ja fast kaum vermeidbar...

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo,

          A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.

          das habe ich auch gelesen. Und wenn das "terminate the process in low-memory conditions" die einzige blöde Nebenwirkung ist, könnte ich das sogar verschmerzen. Aber ...

          Verwendest Du die CRT im Thread?

          Nein. 😀

          Das ist ja fast kaum vermeidbar...

          Würde ich nicht sagen. Für viele CRT-Standardfunktionen hält das Windows-API eine Alternative bereit, etwa wsprintf() anstatt sprintf(), lstrlen() anstatt strlen(), ReadFile() und WriteFile() anstatt fread() und fwrite(), und noch einige Kandidaten in der Art.

          Ich bin gerade nochmal die knapp 600 Quellcode-Zeilen für meine DLL durchgegangen und habe keinen CRT-Funktionsaufruf gefunden.

          Live long and pros healthy,
           Martin

          --
          Home is where my beer is.