Hallo, ich stehe aktuell vor einem kleinen Problem, bei dem ich nicht so recht weiterkomme. Vereinfacht gesagt habe ich ein GUI das einen Start- und einen Stopp-Knopf hat. Mit einem Druck auf Start soll in einer Endlosschleife etwas abgearbeitet werden, bis irgendwann mal wieder Stopp gedrückt wird. Wie löst man so etwas elegant (Threads wären ein Thema, aber eigentlich scheue ich den Aufwand ...)? Aktuell stehe ich ja vor dem Problem, dass bei einmal gedrücktem Startknopf der Stoppknopf nicht mehr erkannt wird, da sich der PC in einer Endlosschleife im Handler des Startbuttons befindet. Eine Timerroutine (angestoßen durch Start, gestoppt durch Stopp) hilft mir auch nicht wirklich, da die Dauer der Abarbeitung des Inhalts der Schleife verschieden lang sein kann und der PC möglichst keine Zeit verschenken soll. Ich hoffe es ist halbwegs klar geworden, wo mein Problem liegt. Alex
Entweder du nimmst Threads oder du sorgst dafür, dass innerhalb deiner langen Schleife gelegentlich die anstehenden Nachrichten abgearbeitet werden. Wie letzteres geht, weiß ich nicht (ich hab mir MFC nie angetan), aber das sollte eine gängige Funktion sein, die man mit den passenden Schlagwörtern sicher in der Doku findet.
das mit den msgs abarbeiten war ein PeekMessage()-DispatchMessage() konstrukt.. was ich so in erinnerung hab... PeekMessage war auf jeden fall drinnen... wobei PeekMessage muss dann im prinzip das machen was PreTranslateMessage macht.. nur eben nur für einen button (also auf ein WM_COMMAND mit richtiger ID warten) ob dieser kunstgriff einfacher ist wie ein thread... das bezweifle ich.. weil dort hast du im einfachsten fall global ein CEvent und machst dann bei start einen thread mit AfxBeginThread auf... da drinnen machst ein while(WaitForSingleObject(....)== irgendwas_mit_timeout) vom programmieraufwand wäre das einfacher und vor allem schneller... wenn du noch mehr infos brauchst... einfach posten inwieweit du ahnung von der mfc hast.. es ist sonst einfach mehr tipparbeit und wird langatmig ;) 73
. "Eine Timerroutine (angestoßen durch Start, gestoppt durch Stopp) hilft mir auch nicht wirklich, da die Dauer der Abarbeitung des Inhalts der Schleife verschieden lang sein kann und der PC möglichst keine Zeit verschenken soll." Verstehe ich nicht so ganz. Dein langer Vorgang muss sich in mehrere ausreichend kleine Vorgänge zerlegen lassen, und jedes Auftreten von WM_TIMER löst einen dieser kleinen Vorgänge aus. Das sieht im Grunde genommen in einem Thread, der über ein Event synchronisiert wird, nicht anders aus; auch dort muss der lange Vorgang in mehrere ausreichend kleine Vorgänge zerlegt werden und zwischen zwei dieser kleinen Vorgängen das Event überprüft werden. Im Thread auf das Event zu warten bedeutete, daß während dieser Zeit der Thread nichts macht, was vermutlich kontraproduktiv ist. Wird als Wartezeit 0 angegeben, wird zwar nicht gewartet, sondern das Event nur gepollt, dann aber kann eben auch statt des Events einfach ein Flag verwendet werden, das vom GUI-Thread gesetzt/gelöscht wird. Es wäre allenfalls möglich, den Thread brutal zu beenden (per TerminateThread), dann aber ist nicht sichergestellt, daß sich der in einem definierten Zustand befindet, Dateihandles ordentlich geschlossen sind etc. Das empfiehlt sich daher nicht. Um nähere auf das Problem eingehen zu können, sind mehr Informationen über den langen Vorgang erforderlich; was ist das, was geschieht da?
huch rufus ich wusste garnicht das sich hier noch ein mfc/win32 geplagter tummelt ;) die Idee mit TerminateThread empfiehlt sich schon deshalb nicht weil folgendes in der msdn steht... TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems: If the target thread owns a critical section, the critical section will not be released. If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent. If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL. sprich das ist eine subtil bösartige funktion die den thread ohne rücksicht auf irgendwas vernichtet ;) beim WaitForSingleObject das ich angesprochen hatte sollte ich vielleicht noch anmerken, dass man es mit einem timeout von 0 aufruft.. das checkt dann nur ob das event gesetzt ist oder nicht... flag gut und schön aber bitte dann wirklich nur einen atomaren datentyp (sprich int, bool,...). alles andere ist wieder gefährlich... CEvent ist schön und ist extra für sowas in der MFC drinnnen ;) also warum nicht das verwenden ;) zum thema thread sollte man dann noch anmerken, dass man sich den thread handle oder das CWinThread objekt merken sollte... dann kann man nämlcih entweder über den handle oder über das objekt (autodelete auf false setzen in dem fall) schaun ob der thread noch da ist... man sollte nämlich mit dem beenden des programms warten bis alle threads beendet sind... 73
Hallo, erstmal vielen Dank für die Kommentare. Habe die Sache mit einem Thread gelöst, war dann wohl doch das einfachste. Start-Button: Setzt Flag = TRUE und startet Thread (AfxBeginThread (Thread, this)); Stopp-Button: Löscht das Flag. Die Routine Thread (vom Typ static) hat die Form: UINT C...Dlg::Thread(LPVOID pParam) { C...Dlg* pObj = (C...Dlg*) pParam; pObj -> Testfunktion (); return 1; } Die Funktion hat dann die Form: unsigned int C...Dlg::Testfunktion (void) { while (Flag_gesetzt) { //mache etwas bis Stopp //gedrückt wird } } Falls jemand nen einfacheren/besseren Weg kennt nur her mit den Ideen. @Rufus Die Funktion sendet ständig Kommandos an eine Steuerung (Eigenentwicklung) und wertet die emfpangenen Daten aus. Um einen realistischen Belastungstest machen zu können soll sie dies mit maximaler Geschwindigkeit für einen relativ langen Zeitraum machen (mehrere Stunden). Ein Timer wäre sicher eine Variante gewesen, dort hätte ich dann aber mit massig Flags hantieren müssen, da der Anwender aus einem großen Kommandovorrat auswählen kann. Ich denke, dass es so einfacher ist. Der Thread läuft so allerdings nach einmaligen Starten für immer, bis die Applikation beendet wird. Dürfte aber kein Beinbruch sein?! :) Alex
Nachtrag: Das Beenden des Threads kann man so realisieren: unsigned int C...Dlg::Testfunktion (void) { while (Flag_gesetzt) { //mache etwas bis Stopp //gedrückt wird } AfxEndThread(NULL, TRUE); } Unterbricht der Stopp-Button die Endlosschleife beendet sich der Thread selbst.
Hat jemand eine schlüssige Erklärung, warum man aus einem Thread heraus nicht UpdateData (...) bzw. UpdateWindow (...) aufrufen kann? Das führt jedesmal zu einem Absturz der Software. Gibt es dafür ein Workaround?
Soweit ich mich erinnere, darf das Handling von GUI-Dingen nur von Threads ausgeführt werden, die über eine Message-Schleife verfügen. Mehrere GUI-Threads sind möglich, aber extrem selten. Von einigen Beispielen in Tutorials/Büchern abgesehen, habe ich noch keine reelle Implementierung gesehen. Üblich ist das Design mit mehreren "Worker-Thread", die GUI-los im Hintergrund vor sich hinwerkeln und einem GUI-Thread. Das ist auch gar nicht so furchtbar mies zu handhaben; ein "Worker-Thread" kann mit PostThreadMessage eine Nachricht an einen anderen Thread (hier also den GUI-Thread) posten. Anstatt aus einem Thread heraus UpdateData/UpdateWindow aufzurufen, würde ich eine Nachricht an den GUI-Thread posten, die dieser auswertet und dann nötigenfalls UpdateData/Window aufruft. Wenn irgendwelche Daten zwischen GUI-Thread und Worker-Threads ausgetauscht werden, sollten Zugriffe auf diese diese durch geeignete Verriegelungsmechanismen (CriticalSection) geschützt werden, da sonst spannende Nebeneffekte auftreten können. Bei einfachen "atomaren" Daten ist das nicht sehr kritisch, bei komplexen Datenstrukturen aber (zu iterierende Listen o.ä.) wird die Fehlersuche dann recht spaßig.
Besten Dank für die Erleuchtung! Dann werde ich wohl ein paar Sachen umschreiben müssen, macht aber nichts. Alex
Das passiert einem immer, wenn man anfängt, mit der MFC zu arbeiten. Die ist halt ... anders. Man kann sich damit arrangieren, aber das braucht einige Weile. Ich habe zu Anfang auch ziemlich übel geflucht, aber damals (gut zehn Jahre her) gab es keine aussichtsreiche Alternative, jedenfalls keine mir verfügbare. Jedenfalls viel Erfolg!
Eine etwas andere Methode ohne Threads, die ich gerne in Dialogen o.ä verwende, ist das IdleTimeProcessing. Unter folgendem Link kann man das Konzept hierfür nachlesen: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_idle_loop_processing.asp Das Dokument ist auch Bestandteil der MSDN. Die While-Schleife stellt hierbei die Endlos-Schleife dar und in dieser Schleife kann deine Logik abgearbeitet werden. Dies stellt aber nur bedingt eine Alternative zur Programmierung mit Threads dar um kleinere Aufgaben, wie z.B. eine einfache Suche + Listen füllen oder die Abfrage einer seriellen Schnittstelle zu implementieren. Gruß Marc
@Marc Das klingt auch nicht schlecht, allerdings ist OnIdle keine Memberfunktion von CDialog. Wie also herankommen? Sorry, aber OOP ist alles andere als mein Fachgebiet :-)
@Alex OnIdle ist eine Memberfunktion von CWinApp. Daher AfxGetApp()->OnIdle( ... ); AfxGetApp() liefert das CWinApp - Objekt der Applikation zurück. Gruß Marc
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.