www.mikrocontroller.net

Forum: PC-Programmierung VC++ mit den MFC


Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Rufus T. Firefly (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
.
  "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?

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: wgoedde (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
könnte so aussehen...

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: Rufus T. Firefly (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Besten Dank für die Erleuchtung! Dann werde ich wohl ein paar Sachen
umschreiben müssen, macht aber nichts.


Alex

Autor: Rufus T. Firefly (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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!

Autor: Marc Braun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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=...

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

Autor: Alex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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 :-)

Autor: Marc Braun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Alex
OnIdle ist eine Memberfunktion von CWinApp.

Daher
AfxGetApp()->OnIdle( ... );
AfxGetApp() liefert das CWinApp - Objekt der Applikation zurück.

Gruß
Marc

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.