Hilfe bei Threads!!!
-
monne81 schrieb:
Ich hasse Threads!!!!
es muss heissen: Ich hasse MFC!!!!
-
net schrieb:
monne81 schrieb:
Ich hasse Threads!!!!
es muss heissen: Ich hasse MFC!!!!
-
Morggäääännn,
Nachdem ich heute nacht nur von Threads und grobpixeligen Frauen geträumt habe,
ist mir die ganze Sache etwas klarer geworden. Jedoch noch nicht so klar wie eisgekühlter Korn an einem Sonntagmorgen.für meine Anwendung brauche ich erstmal einen Worker Thread, oder?
ich habe mir jetzt eine Threadfunktion gebaut:
UINT CDlgAnalogIn::thread_function(LPVOID pParam) { CDlgAnalogIn* pDlg = (CDlgAnalogIn*) pParam; pDlg->SetValue(); return 0; }
diese rufe ich im Dialog auf:
m_xDisplayThread = (CMyThread*)AfxBeginThread(thread_function, this);
sprich ich kann mir die ganze Klasse CMyThread sparen.
Leider bekomme ich eine Assertion Failed Message!!!!!
Jetzt noch eine grundlegende Frage:
wenn ich den Thread (userinterface Thread??) über eine Klasse definiere und so aufrufe:
CMyThread* m_xDisplayThread; //m_nTimer=SetTimer(1,200,0); m_xDisplayThread = (CMyThread*)AfxBeginThread(RUNTIME_CLASS(CMyThread), NULL, 0, CREATE_SUSPENDED); // m_xDisplayThread = (CMyThread*)AfxBeginThread(thread_function, this); m_xDisplayThread->SetOwner((CMyThread::CDlgAnalogIn*)this); m_xDisplayThread->ResumeThread();
wird der Thread ja über ResumeThread() gestartet
In welche Funktion springt mein Thread dann? bzw. ich habe in meiner Threadklasse eine Methode, die ausgeführt werden soll, nur wie rufe ich die auf???
-
Du brauchst entweder einen Worker-Thread, der nach dem Start still vor sich arbeitet und eventuell Werte an seinen Chef zurückgibt (über den mitgelieferten Parameter) oder einen Interface-Thread, der ein Fenster erstellt und darüber mit dem Nutzer kommunizieren kann.
In deinem Beispiel benötigst du wohl doch einen Interface-Thread - allerdings mußt du ihn vom Hauptprogramm (CDlgAnalog oder CAnalogApp) aus starten und dann innerhalb der InitInstance()-Methode deinen Dialog anlegen.
-
Hi,
genügt nicht ein Worker Thread? der thread soll ja nur im Hintergrund die Werte vom Analog Input einlesen einen Mittelwert bilden und diesen Mittelwert dann an den Dialog CDlgAnalogIn senden.
Leider bekomm ich immer noch die Assertion Failed und hab keine Ahnung wieso!
wenn ich step by step durchgehe liest er einmel den Port aus zeigt die Werte an und beim 2ten Mal Berechnen (Aufruf von SetValue()) kommt die Assertion!
-
Steht da nicht noch eine Zusatzinfo in der Assertion Failed Meldung? (Dateiname, Zeilennummer)
-
monne81 schrieb:
genügt nicht ein Worker Thread? der thread soll ja nur im Hintergrund die Werte vom Analog Input einlesen einen Mittelwert bilden und diesen Mittelwert dann an den Dialog CDlgAnalogIn senden.
Achso, das geht natürlich auch. In dem Fall dürfte dein Problem wohl in der SetValue()-Funktion zu finden sein: Der Dialog (also was du auf dem Monitor siehst) darf nur von dem Thread direkt angesprochen werden, der ihn erzeugt hat (und das ist der Hauptthread).
Lösung: SetValue() speichert nur intern ab, was sich geändert hat und signalisiert dann dem Hauptthread, daß etwas passiert ist:
void CDlgAnalogIn::SetValue(int data) { m_data = data; PostMessage(WM_DATACHANGED); } LRESULT CGldAnalogIn::OnDatachanged(WPARAM w,LPARAM l) { //stelle den Wert von m_data im Dialog dar }
(WM_DATACHANGED ist eine selbstdefinierte Nachricht, OnDatachanged die zugehörige Behandlungsroutine)
-
Also meine Assertion Failed sagt mir folgendes:
Dolmetscher:File
wincore.cpp, Line1051Verstehe ich das richtig, dass ich mit meiner thread_function den Dialog nicht ansprechen darf, weil thread_function den Dailog nicht erstellt hat????
ich probiers mal aus, ich glaub ich habs...
Noch eine Frage:
ich glaub die Werte werden ziemlich häufig erneuert (Signal am Port ist Audiosignal) kann das zu Problemen führen? Die WM wir dann ja ziemlich häufig aufgerufen....
-
monne81 schrieb:
Also meine Assertion Failed sagt mir folgendes:
Dolmetscher:File
wincore.cpp, Line1051Dann klick mal auf "Abbrechen" (das setzt den Debugger an die Stelle, wo der Fehler aufgetreten ist) und wandere ein paar Stufen im Call-Stack nach oben.
Verstehe ich das richtig, dass ich mit meiner thread_function den Dialog nicht ansprechen darf, weil thread_function den Dailog nicht erstellt hat????
Ja, so sieht's aus: die Funktion darf zwar interne Daten umstellen, aber nicht direkt (oder indirekt) auf die Dialogelemente zugreifen - deshalb der Umweg über PostMessage (der MessageHandler wird bei nächster Gelegenheit im Rahmen der Nachrichtenschleife aufgerufen, und die sitzt im richtigen Thread).
-
tatat....
Danke!!! funktioniert alles prima....
bis auf eine "Kleinigkeit":
Wenn ich den Dialog beende soll der Thread auch beendet werden, ich mach dies mit
void CDlgAnalogIn::OnOK() { b_running=false; delete m_xDisplayThread; CDialog::OnOK(); }
dann bekomm ich aber wieder eine Assertion Failed!
Darf ich den Thread so beenden?
-
monne81 schrieb:
Darf ich den Thread so beenden?
Kurz: Nein.
Setz' besser eine Variable, die der Thread wiederfindet und lass ihn sich dann selber beenden (AfxEndThread()).
PS: Weitere Informationen in der MSDN
-
Ja ich hab es selber gefunden, sorry hab die Frage (wohl aus Bequemlichkeit) zu früh gestellt!
ich versuch zu warten (WaitforSingleObject) bis der Thread fertig ist
und dann beenden. Funktioniert aber nicht!!void CDlgAnalogIn::OnOK() { m_thread_active=false; b_running=false; CString warning= "Fehler Thread nicht beendet"; if(WAIT_TIMEOUT == WaitForSingleObject(m_xDisplayThread, 5000)) { AfxMessageBox(warning, MB_OK); } else { AfxEndThread((unsigned int)thread_function, NULL); CDialog::OnCancel(); } //CDialog::OnOK(); }
das mit der variable m_thread_active funktioniert auch nicht!
} void CDlgAnalogIn::SetValue() { p_ai->sum=0; float value; if(m_thread_active) { while(b_running) { Sleep(500); value=(p_ai->ReadAnalogPort()); m_data=value; PostMessage(WM_DATACHANGED); } } // else // AfxEndThread((unsigned int)thread_function, NULL); }
-
Falscher Parameter für AfxEndThread() - du bist im Thread deiner Thread-Function, also brauchst du deren Adresse nicht mehr anzugeben. Schreib doch einfach "AfxEndThread(0);"
Oder sogar noch einfacher - teste die Abbruchbedingung direkt in der Thread-Funktion:
UINT thread_func(LPVOID Param) { CDlgAnalogIn* reciever=(CDlgAnalogIn*)Param; while(reciever->m_thread_active) { float val=ReadAnalogPort(); reciever->SetValue(val); } }
-
tja ich bin dir da schon zuvor gekommen:
//Thread starten UINT CDlgAnalogIn::thread_function(LPVOID pParam) { CDlgAnalogIn* pDlg = (CDlgAnalogIn*) pParam; if(pDlg->m_thread_active) { pDlg->SetValue(); } else AfxEndThread(0); return 0; }
-
Dir ist aber klar, daß diese Funktion genau einen Wert von deinem Analogport liest und dann den Thread wieder beendet? Für so etwas sind eigenlich keine eigenständigen Threads notwendig.
-
Sorry, ich hab das falsche kopiert....
es sieht natürlich so aus:
//Thread starten UINT CDlgAnalogIn::thread_function(LPVOID pParam) { CDlgAnalogIn* pDlg = (CDlgAnalogIn*) pParam; while(pDlg->m_thread_active) { pDlg->SetValue(); } //AfxEndThread(0); return 0; }
Berechnung:
void CDlgAnalogIn::SetValue() { p_ai->sum=0; float value; Sleep(100); value=(p_ai->ReadAnalogPort()); m_data=value; PostMessage(WM_DATACHANGED); }
dazu mein beenden Methode:
void CDlgAnalogIn::OnOK() { m_thread_active=false; b_running=false; CString warning= "Fehler Thread nicht beendet"; // WaitForSingleObject(m_xDisplayThread, 5000); AfxEndThread(0); CDialog::OnOK();
Was jetzt passiert verwirrt mich allerdings:
das Auslesen funktionirt wunderbar,
sobald ich auf OK klicke, wird m_thread_active auf false gesetzt, dann springt er plötzlich in Set value, springt dann wieder in OnOk, geht auf AfxEndThread, springt dann wieder in SetValue und stürzt dann natürlich ab da Thread ja beendet!!!
-
Du mußt AfxEndThread() immer aus dem Thread heraus aufrufen, den du gerade beendest (return in der Thread-Funktion hat den selben Effekt) - also ist der Aufruf in der OnOK()-Methode überflüssig (und beendet vermutlich den Hauptthread deines Programms).
PS: Und es wäre eine gute Idee, die gegenseitigen Elementzugriffe mit einer CriticalSection voreinander abzuschirmen - es kann sehr unangenehm werden, wenn du mit halbfertig geschriebenen Daten arbeiten willst.
-
so Probblem:
ich setz die bool m_thred_active auf false,
dann wird die while - schleife beendet und er geht auf return 0 und dann auf OnDialog:Ok und verlässt den Dialog, ist in meinem Hauptfenster und geht dann noch mal auf SetValue() und postMessage und stürzt dann natürlich ab!Warum springt er trotz Thread beenden und Dialog schließen noch mal in die SetValue- Methode??????
//Thread starten UINT CDlgAnalogIn::thread_function(LPVOID pParam) { CDlgAnalogIn* pDlg = (CDlgAnalogIn*) pParam; while(pDlg->m_thread_active) { pDlg->SetValue(); } return 0; }[cpp]void CDlgAnalogIn::OnOK() { m_thread_active=false; b_running=false; thread_function(this); // WaitForSingleObject(m_xDisplayThread, 5000); // delete m_xDisplayThread; //AfxEndThread(0); CDialog::OnOK(); }
-
So hab jetzt die Lösung (für denjenigen den es interessiert):
void CDlgAnalogIn::OnBUTTONStop() { DWORD state; m_thread_active=false; b_running=false; state=WaitForSingleObject(*m_xDisplayThread, 1000); if(state==WAIT_TIMEOUT) { //Thread läuft noch Thread beenden OnBUTTONStop(); } else//thread läuft nicht -> Dialog verlassen CDialog::OnOK(); }
beim Drücken von Stop soll der Dialog beendet werden und das Einlesen des AnalogPorts auch.
Zuerst werden die Flags auf False gesetzt
Dann kommt eine Abfrage ob sich der Thread noch "meldet"
wenn ja nochmal Zeit geben um ausstehende Operationen durchzuführen,
andernfalls kann man den Dialog beenden da der Thread zu ende ist.
-
Du solltest lieber mit einer while-Schleife arbeiten, anstatt Dich selber rekursiv aufzurufen, da bei langen Wartezeiten u.U. der Stack überlaufen kann.
Und selbst die while-Schleife sollte nach gewisser Zeit abbrechen, falls sich der Thread aufgehangen hat.