Threads Synchronisieren



  • Halli hallo,

    hab da ein Problem mit nem Thread in meinem Prog. und zwar habe ich nen Thread den ich Folgendermaßen aus meiner VIEW Klasse aufrufe:

    m_ThreadParam.m_Flag=1;
    	m_ThreadParam.hMainWin = m_hWnd;
    	m_MeinThread = AfxBeginThread (CPC.connect, &m_ThreadParam);
    

    Nun ist es so, das ich aus dem einem Thread in den anderen per POSTMESSAGE eine Nachricht schicke, die dann im Dialog in einer CListBox etwas ausgeben soll.
    Dazu lese ich die informationen die er ausgeben soll aus einer .txt Datei aus und sende Sie halt per POSTMESSAGE an die ListBox.
    Momentan ist es aber so, das er manchmal das richitge ausgibt, und manchmal dreimal hintereinander ein und das gleiche.

    Ich denke das liegt daran das die Threads nicht Synchron laufen, ODER ?? 😕

    Kann da jmd was zu sagen ??

    BIG THX im voraus !!!!!!!!!

    Slawo


  • Mod

    Ich vermute mal, dass Du einen Zeiger in PostMessage verwendest. Evtl. auf ein Speichersegment im Stack. Da der Stack weiterverwendet wird und die Nachricht irgendwann asynchson ankommt ist der Speicher längst wiederverwendet.
    Du musst Dir hier eine eigene Verwaltung einfallen lassen, die garantiert, dass die Daten nicht verändert wurden.

    Ich verwende oft einfach eine Liste, die über eine CCriticalSection abgesichert wird. In diese schreibt der Thread seine Daten. Sendet per PostMessage eine Info, das neue Daten da sind, und der Hauptthread liest die Daten aus der Liste und löscht sie.



  • ohh man 😮 , hört sich ja höchst kompliziert an. mein PostMessage sieht so aus :

    PostMessage(((PTHREADPARAM)pParam)->hMainWin, WM_NACHRICHT, 0, 0);
    

    und ist so definiert:

    ON_MESSAGE(WM_NACHRICHT, MeineNachricht)
    
    LRESULT CBVSView::MeineNachricht(WPARAM wparam, LPARAM lparam)
    {
    	switch(wparam)
    	{
    		case 0: {CString statstr;
    				 CStdioFile f;
    				 f.Open("StatusLog.txt", CFile::modeRead | CFile::typeText);
    				 f.ReadString(statstr);
    				 m_ListBoxAusgabe.AddString(statstr);
    				 f.Close();
    				}break;
    		case 1:{m_ListBoxAusgabe.ResetContent();}break;// ListBox leeren
    
    	}
    
    	return 1;
    }
    

    Bzw. gibt es eine andere möglichkeit Daten bzw. Strings, denn es sind eigentlich nur Strings die ich da versende, zwischen zwei threads auszutauschen?
    Was ich also eigentlich machen will ist folgendes. Ich möchte soeine art Status ausgabe in m einem Programm machen, und das soll immer in der ListBox ausgegeben werden.Sprich wenn irgendetwas passiert, z.B. ein Wert wurde berechnet, etwas wurde in der DB gespeichert usw. möchte ich das halt mitkriegen und das dann in der ListBox ausgeben, damit ich sehe was da gerade bei mir im Programm passiert.
    Das Problem ist halt, wie schreibe ich aus dem Neuen Thread in die ListBox die in dem Hauptthread drinne ist.
    Mit

    m_ListBox.AddString("juhu");
    

    geht das ja nicht Threadübergreifend.

    Gibt es da vielleicht noch einen anderen Ansatz den man verfolgen könnte ??? Vielleicht mit nem Editfeld oder so?

    Bzw. würde es helfen wenn ich die beiden Threads irgendwie Synchronisieren würde? (Aber wie 😕 😕 😕 )



  • Wieso soll das nicht Theadübergreifend gehen. Siehe FAQ. Ableiten von CWInThread.
    Zeiger auf den Dialog übergeben. Auf Zeigen->ListBox zugreifen.
    Wenn du auch noch was anderes mit der Listbox machst dann Sync.


  • Mod

    Unix-Tom schrieb:

    Wieso soll das nicht Theadübergreifend gehen. Siehe FAQ. Ableiten von CWInThread.
    Zeiger auf den Dialog übergeben. Auf Zeigen->ListBox zugreifen.
    Wenn du auch noch was anderes mit der Listbox machst dann Sync.

    Nein es geht nicht. Dann fliegt ihm beim AddString ein ASSERT um die Ohren weil diw CWnd Objekte in einer threadlokalen Map hinterlegt werden.
    Zudem würde dies eine direkte synchronisation bedeuten. Dann kann man sich die ganze Sache mit dem Thread fast auch sparen.

    @Slawo:
    Du schreibst vermutlich die Daten in die Datei und führst PostMessage durch. Dein Pr0oblem ist aber dass evtl. die Daten noch nicht abgehöt sind, wenn Dein Thread die selbe Datei wieder öffnet und wieder etwas hineinschreibt. Daher werden öfters die Daten doppelt angezeigt, weil der Fensterthread die evtl. gleich zweimal hintereinander Deine Nachricht bearbeitet.
    Ich bleibe bei menem Vorschalg. Der ist in jedem Falle einfacher als Dein Konstrukt und bedarf nicht der Festplatte als Austauschmedium.



  • Na wenn du meinst. Ich habe hier mehrere Programme (Serverprogramme) welche seit Jahren so laufen.
    Wenn der Hauptthread nichts im Steuerelement macht braucht man nichtmal zu synchronisieren.
    Und ob CWnd Objekte in einer threadlokalen Map hinterlegt werden spielt dabei keine Rolle weil man dem Thread (nennt sich dann übrigens Benutzeroberflächenthread und ist von CWinThread abgeleitet) einen Zeiger auf den Dialog übergibt
    Aber vermutlich weißt Du mehr.



  • Ich würde dem Thread eine Adresse mitgeben, in die er seine Strings eintragen kann (z.B. ein privates Element der Fensterklasse) - dann kann er den String dort einfügen und den "Chef" benachrichtigen:

    class CMyView : public CView //oder so ähnlich
    {
    private:
      std::list<CString> statusdata;
    
      static int ThreadFunc(LPVOID param)
      {
        CMyView* mthis=(CMyView*) param;
        ...
        CString status="OK";
        mthis->statusdata.push_back(status);
        mthis->PostMessage(WM_CHANGED,0,0);
      }
    
      void OnChanged()//Nachrichtenbehandlung WM_CHANGED
      {
        CString status=statusdata.front();
        ...
        statusdata.pop_front();//fertig verarbeitet - kann gelöscht werden
      }
    };
    

  • Mod

    Genaus so würde ich es machen. Nur musst Du den Zugriff Lesend/Schreibend auf die Liste mit einer Critical Section absichern! STL Objekte im allgemeinen nicht thread safe!



  • Muß es unbedingt PostMessage sein? Es geht doch auch SendMessage

    CString Text("Zeig es an");
    SendMessage(((PTHREADPARAM)pParam)->hMainWin, WM_NACHRICHT, 0, (LPARAM)(LPCTSTR)Text);
    

    und ist so definiert:

    ON_MESSAGE(WM_NACHRICHT, MeineNachricht)
    
    LRESULT CBVSView::MeineNachricht(WPARAM wparam, LPARAM lparam)
    {
    	switch(wparam)
    	{
    		case 0: if(lparam)
    			m_ListBoxAusgabe.AddString(statstr);
                                break;
    		case 1:{m_ListBoxAusgabe.ResetContent();}break;// ListBox leeren
    
    	}
    
    	return 1;
    }
    

    SendMessage wartet auf das ergebnis der gesendeten Message also die Message wird erst verarbeitet, wenn der Thread (das Hauptwindow) wieder Leistung bekommt und damit ist die Message Threadsafe. Anderweitige Meinung würden mich Interessieren!

    Gruß


  • Mod

    CTecS schrieb:

    Muß es unbedingt PostMessage sein? Es geht doch auch SendMessage

    SendMessaage hat den gravierenden Nachteil, dass dann der Workerthread nicht weiter läuft bis der andere Thread die Nachricht bearbeitet hat. Dann benötigt man auch keinen extra Thread. SendMessage für zu einer direkten Threadsynchronisation. D.h. wird in diesem Moment im anderen Thread keine Nachrichtenschleife ausgeführt, dann steht der Workerthread.


Log in to reply