Der richtige Platz für Aufräumarbeiten



  • Meine Grundkonstellation:
    Ich habe eine Dialog-Anwendung, die im Hintergrund einen Arbeitsthread laufen lässt:

    //Hauptfunktion - nicht veränderbar
    void thread_func(...,bool(*callback)(void*,...),void* caller,...)
    {
      //Vorbereitung
      while(!finish)
      {
        do_something;
        if(!callback(caller,...)) break;
      }
    }
    
    //die verwendete Callback-Funktion (static)
    bool CMyDialog::callback(void* me,...)
    {
      CMyDialog* pThis = (CMyDialog*) me;
      ...
      pThis->SendMessage(WM_UPDATE,0,0);
      //WM_UPDATE ist eine WM_APP-Nachricht, die der Dialog entgegennimmt
      //in der OnUpdate() werden die Ergebnisse des letzten Rechenschrittes dargestellt
    
      HANDLE events[]={pThis->m_run,pThis->m_stop};
      //m_run = nächster Schritt, m_stop = Abbruch
      return WaitForMultipleObjects(events,2,FALSE,INFINITE)==WAIT_OBJECT_0;
    }
    
    void CMyDialog::stop()
    {
      m_run.ResetEvent();
      m_stop.SetEvent();
      WaitForSingleObject(m_thread/*Handle auf obigen Thread*/,INFINITE);
    }
    

    Mein Problem ist jetzt, wo soll ich die stop()-Methode aufrufen, ohne mir damit das Programm zu zerschießen (das Hauptproblem vermute ich in dem SendMessage()-Aufruf):

    • PostNCDestroy() spuckt mir eine Access Violation aus, weil SendMessage() kein Ziel mehr hat
    • OnClose() führt zu einem Deadlock (der Anzeigethread wartet auf den Arbeitsthread - und der wartet darauf, daß sein WM_UPDATE verarbeitet wird)

    Alternativ dazu:
    Kann ich von meiner Callback-Funktion aus feststellen, ob ich noch ein Fenster habe?


  • Mod

    Da Dein Thread aber Nachrichten versendet und auf deren Bearbeitung wartet (SendMessage) hast Du das Problem.
    Warum ist ein PostMessage nicht möglich?

    Du kannst natrülich auch MsgWaitForMultipleObjects verwenden und weiterhin die Nachrichtenschleife am Leben erhalten, und das sogar nur für die Nachrichten, die es hier betrifft (PeekMessage auf WM_UPDATE).

    Ich setze nur das Stop Ereignis und prüfe zyklisch (z.B. Timer oder andere Konstrukt in der Nachrichtenschleife) ob der Thread terminiert ist.

    Oder dich interessiert gar nicht, das der Thread fertig ist. Warum willst Du es wissen? 🙂

    BTW: Events für run halte ich für überflüssig. Man muss nicht mal ein Event nehmen um den Stop auszulösen, auch da genügt ein volatile Flag.



  • Martin Richter schrieb:

    Da Dein Thread aber Nachrichten versendet und auf deren Bearbeitung wartet (SendMessage) hast Du das Problem.
    Warum ist ein PostMessage nicht möglich?

    Weil die Nachricht erst nach dem WM_CLOSE abgearbeitet werden sollte - und mir dabei ebenfalls um die Ohren geflogen ist.

    (btw, ich hab' das Problem inzwischen entschärft, indem ich SendMessageTimeout() verwendet habe)

    Oder dich interessiert gar nicht, das der Thread fertig ist. Warum willst Du es wissen? 🙂

    Technisch gibt es zwei Gründe dafür (entsprechen den zwei Möglichkeiten, wo ich stop() aufrufen will):

    1. möglicher Neustart - bevor ich die Berechnung (mit anderen Parametern) wieder in Gang setzen kann, muß der alte Thread sauber beendet werden, sonst kommen sie sich gegenseitig in die Quere.
    2. sauberer Programmabschluß - klar kann ich auch darauf warten, daß Windoof den Thread killt, nachdem mein Hauptprogramm beendet wurde. Aber das halte ich nicht gerade für sauberen Programmierstil.

    BTW: Events für run halte ich für überflüssig. Man muss nicht mal ein Event nehmen um den Stop auszulösen, auch da genügt ein volatile Flag.

    Gewohnheitssache - bei Events muß ich mich nicht um die Synchronisierung kümmern. (und der run-Event dient dazu, einen Einzelschritt-Modus auszuführen - oder die Berechnung zwischnzeitlich pausieren zu lassen)


  • Mod

    Hier ist es doch einfacher ein Flag zu setzen ob der Dialog noch da ist!
    Wenn der geschlossen wurde kanst Du ja wahlfrei auf das PostMessage verzichten und terminierst automatisch.
    Wenn Dein Dialog schließt, kann er selbst mit PeekMessage und PM_REMOVE ausstehende Benachrichtigungen abfangen (aufessen).

    Die Frage wäre ob Du Deine Interaktion zwischen Thread und GUI nicht anders aufbaust.
    Nach dem Motto, Du hast einen Timer, der ab und zu in der GUI nachsieht ob der Thread Infos hat. Wenn ja zeigt er diese an. Dann benötigt der Thread gar keine Methode mehr um den Dialog zu benachritigen. Die Daten die der Thread errechnet müssen ja sowieso mit CCriticalSection geschützt werden.


Anmelden zum Antworten