Timer und while-Schleife asynchron laufen lassen



  • Derzeit versuche ich mittels klick auf einen Button einen Timer zu starten. Dann geht eine While-Schleife los, die solange läuft, bis der Timer getickt hat.

    Leider jedoch funktioniert das nicht, denn die While-Schleife hört nicht auf, obwohl der Timer schon längst getickt haben müsste.

    Ich Vermute, das liegt daran, das Timer und While-Schleife nicht gleichzeitig abgearbeitet werden können, d.h. das Timer Tick Event tritt erst ein, wenn die While Schleife abgelaufen ist(was aber nicht eintreten kann, ohne das das Timer Tick Event eintritt.

    Weiß jemand eine Lösung?



  • Du musst Deine Schleife in einem anderen Thread laufen lassen, sonst wird der Timer nie ablaufen, da er nie den Thread benachrichten kann...



  • Und wie mache ich das?
    Ich habe mich noch nie mit Threads befasst 🙄



  • WAS willst Du überhaupt genau machen?



  • Ich möchte gleichzeitig einen Timer sowie eine While-Schleife laufen lassen.

    Diese While-Schleife endet erst, wenn der timer getickt hat.



  • Hallo MR. X,

    bau Deine while Schleife so, so müsste auch der Timer zum Zug bei der CPU kommen.

    do
    {
    
        Application::DoEvents();
        //Anweisung
    }
    while(loopEnd);
    

    Gruß, Doug_HH



  • Leider nein.
    Durch setzen von Breakpoints sehe ich, das das Programm in der Schleife zirkuliert



  • Mr X schrieb:

    Ich möchte gleichzeitig einen Timer sowie eine While-Schleife laufen lassen.

    Das wissen wir jetzt. Aber was möchtest Du damit erreichen?

    Im Moment sieht es so aus, als wolltest Du die Oberfläche blockieren, sprich keine Signale bearbeiten, bis eine bestimmte Zeit abgelaufen ist. Das widerspricht allerdings der Natur eines Forms-Timers, der nämlich selbst ein Ereignis auslöst.

    Oder möchtest Du wirklich eine Hintergrundverarbeitung, während der die Oberfläche nicht blockiert? Dann wärst Du allerdings auf dem falschen Weg.



  • WAS möchtest Du in der Schleife machen???

    Und dass das Program min der Schleife "zirkuliert" ist nun mal der Sinn und Zweck einer Schleife, oder?



  • Ja auf sicher läuft die Schleife dadurch weiter.
    Die Schleife wird so lange laufen bis die Bedingung erfüllt ist.
    Egal für welche Schleife Du Dich entscheidest.

    Fakt ist, dass der Befehl Application::DoEvents(); nicht die Abbruchbedingung für die Schleife ist.
    Dieser Befehl ist dafür da, um Befehle und Ereignisse aus der Warteschlange vom Betriebssystem zu holen.

    Ohne Application::DoEvents(); wird kein Befehl oder Ereignis aus der Warteschlange vom Betriebssystem geholt und abgearbeitet.
    Im Klartext bedeutet das, wenn die Schleife ohne Application::DoEvents(); ausgeführt wird, und die Abbruchbedingung nicht erfüllt wird, weil die Variable zum Beispiel nie auf false gesetzt wird, kommst Du nie sauber aus dieser Schleife raus.
    Da hilft dann nur noch Strg+Pause.

    Oder Du lässt die Schleife in einem anderen Thread laufen, wie das Jochen schon gesagt hat.

    Gruß, Doug_HH



  • Danke jetzt habe ich es kapiert 🙂

    Ich hatte Application::DoEvents();
    für irgendetwas was stellvertretend für irgendwelche Anweisungen steht gehalten... 🙄



  • Auch wenn DoEvents recht verlockend für sowas aussieht, sollte man dieses, aus VB leider mitverschleppte Übel, lieber nie einsetzen.
    Nicht umsonst gibts die schöne kleine Anmerkung in der MSDN Lib:

    Calling this method can cause code to be re-entered if a message raises an event.

    Was an sich relativ harmlos klingt kann einen die ganze Programmlogik sowas von zerschissen dass man sich danach ärgert überhaupt mal diese Funktion kennen gelernt zu haben. In VB gabs die auch nur weil man da nicht vernünftig mit Threads arbeiten konnte, an sich gibt es keine Situation wo man diese Methode unbedingt benutzen muss.

    Auch wenn mir nicht klar ist worauf das mit der Schleife und dem Timer hinauslaufen soll, ist die Lösung die Jochen Kalmbach gleich in er ersten Antwort angedeutet hat mit nem extra Thread für die Schleife die viel besser als DoEvents.

    Allgemein laufen die Timer übrigens in nem extra Thread, nur wenn man den System.Windows.Forms.Timer verwendt werden die Eventshandler automatisch in den GUI Thread dispatched und nützen bei einer blockierenden Schleife dann gar nichts. Aber da gibts noch den System.Timers.Timer der besser geeignet wäre.



  • Talla schrieb:

    Auch wenn mir nicht klar ist worauf das mit der Schleife und dem Timer hinauslaufen soll, ist die Lösung die Jochen Kalmbach gleich in er ersten Antwort angedeutet hat mit nem extra Thread für die Schleife die viel besser als DoEvents.

    Gib doch bitte mal ein Bsp. mit den Threads.

    Gruß, Malle



  • Naja, Beispiele zu Threads gibts auch massig in der MSDN Lib, aber ich bin mal nicht so:

    using System;
    using System.Threading;
    using System.Timers;
    
    namespace ConsoleApplication2 {
        class Program {
            static void Main(string[] args) {
    
                bool abort = false;
                System.Timers.Timer timer = new System.Timers.Timer(2500);
    
                timer.Elapsed += (x,y) => {
                    timer.Stop();
                    Console.WriteLine("Timer stops loop...");
                    abort = true;
                };
    
                Thread t = new Thread( () => {
                        while (!abort) {
                            Console.WriteLine("Hello from loop");
                            Thread.Sleep(250);
                        }
                    }
                );
                timer.Start();
                t.Start();
                t.Join();
                Console.WriteLine("Press Enter to exit");
                Console.ReadLine();
            }
        }
    }
    

    Mal ein klein wenig anspruchsvolleres Beispiel das auch mal Lambda Ausdrücke und vor allem Closures demostriert. Bissle was zum durchdenken für Einsteiger warum das Programm überhaupt funktioniert 🙂 (Das könnte man auch mit anonymen Delegates machen damit es auch mit .Net 2.0 funktioniert, aber sieht dann nimmer so schön aus *gg*)

    EDIT: Argh, hab voll verpeilt das des ja das C++/CLI Forum ist, zwar auch .Net aber net C#, aber denke vom Prinzip her ist der Code ja auch in C++ umsetzbar.



  • Ich versuche gerade meine Schleife in einen Thread einzubauen, leider scheitere ich jedoch daran, dass sich scheinbar nur Methoden von Klassen in einem anderen Thread starten lassen. Stimmt das, bzw. wie kann ich das so lösen wie in Tallas c# Beispiel?



  • Dein Code ist absoluter Müll, lass es lieber...
    Bringt nix...
    Guck lieber selber nochmal in die Bücher bevor Du hier so auf die Platte drückst.
    Dann schreib lieber nix als so einen Müll.
    Das haut ja hinten und vorne nicht hin.
    Das wäre mir persönlich sowas von peinlich.

    Gruß, Malle



  • Malle schrieb:

    Dein Code ist absoluter Müll, lass es lieber...

    Sicherlich kann man den Code immer verbessern, aber er erfüllt die Anforderungen des Fragestellers, Timer und Thread laufen unabhängig voneinander und wenn das Timerevent feuert, wird die Schleife im Thread beendet.
    Wieso hälst du den Code für so schlecht dass du mit deiner Aufforderung nochmal lieber in Bücher zu schaun, mich persönlich angreifst? Rumlabern und schlechtmachen kann jeder, gekonnt ist sowas nur wenn mans auch begründen kann.

    Das es ausversehen die falsche Sprache war, hatte ich ja schon geschrieben, aber hier mal ne C++/CLI Version:

    using namespace System;
    using namespace System::Threading;
    using namespace System::Timers;
    
    bool abort = false;
    
    void OnTimedEvent( Object^, ElapsedEventArgs^ ) {
    	Console::WriteLine( "Timer stops loop..." );
    	abort = true;
    
    }
    
    void DoWork(){
    	while(!abort) {
    		Console::WriteLine( "Hello from loop" );
    		System::Threading::Thread::Sleep(250);
    	}
    }
    
    int main(array<System::String ^> ^args) {
    	System::Timers::Timer^ timer = gcnew System::Timers::Timer(2500.0);
    	timer->Elapsed += gcnew ElapsedEventHandler(OnTimedEvent);
    	timer->AutoReset = false;
    	timer->Enabled = true;
    	Thread^ newThread = gcnew Thread( gcnew ThreadStart( &DoWork ) );
    	newThread->Start();
    	newThread->Join();
    	Console::WriteLine(L"Press Enter to Exit");
    	Console::ReadLine();
    	return 0;
    }
    

    Der Code ist nur halbherzig übersetzt, da er wirklich nicht sehr .Net like ist, durch Verwendung der globalen Variable z.B.. Nichtsdestotrotz bleibt das Grundprinzip genau gleich. Die C++/CLI Variante ist bei weitem auch nicht so elegant was z.b. die anonymen Delegates mit den Closures angeht (wobei ich statt Delegates ja wegen der schöneren Syntax Lambda Expressions verwendet habe) - die C# Lösung gefällt mir persönlich da mehr. Aber egal, beide lösen das Problem *gg*



  • Hallo Talla,

    die Argumente von „Malle“ sind voll daneben.
    In erster Linie mag ich absolut keine User die unregistriert was ins Forum kritzeln.

    Zum zweiten lässt sich der C# Code in der Tat so nicht übersetzen.
    Da zum einem ist dieses Zeichen => dem Compiler nicht bekannt, sowie die Variablen x,y sind unbekannt.
    Ich weiß nicht ob Du uns eine kleine Denksportaufgabe geben wolltest, oder den Code selber nicht übersetzt hast?
    Was ja auch nicht schlimm ist.

    Na gut, dass ist aber auch nicht der Grund warum ich mich hier zu Wort melde.
    Sondern, dass ich bis zum jetzigen Zeitpunkt in der Tat nichts negatives gefunden habe was gegen DoEvents sprechen sollte.

    Warum ist es nicht richtig die Warteschlange des Betriebssystems auf diese Art und Weise vorzeitig abzufragen?
    Warum sollte ich damit warten, bis die Schleife (Prozedur) abgeschlossen ist.
    In einigen Fällen ist das eine Endlosschleife oder ein rekursiver Aufruf und die CPU wird von dieser Prozedur blockiert.

    Das so von Dir hochgelobte Threading hat nicht so einen guten Ruf.

    MSDN schrieb:

    Potenzielle Probleme bei Multithreadingcode
    Nachdem Sie den Beispielcode durchgearbeitet haben, verfügen Sie nun über die Tools, die für das Schreiben Ihrer eigenen Multithreading-Anwendung erforderlich sind. Threading kann sowohl die Leistung als auch die Skalierbarkeit bestimmter Anwendungen drastisch steigern. Sie sollten sich jedoch auch der Gefahren des Threading bewusst sein. Es gibt einige Situationen, in denen die Verwendung von Threads Ihrer Anwendung schaden kann. Threads können den Betrieb verlangsamen, unerwartete Ergebnisse auslösen oder sogar bewirken, dass Ihre Anwendung nicht mehr reagiert.

    http://msdn2.microsoft.com/de-de/library/aa289523.aspx

    Also mein Zitat ist bislang, es sei den einer kann mich vom Gegenteil überzeugen.

    Dass die "DoEvents-Methode" sicherer ist, als von einem „Single-Threading“ in ein „Multi-Threading“ abzurutschen.

    Fakt ist, wenn ich als Neuling in einem Forum lese, dass DoEvents schlecht ist und man es bereut es jemals verwendet zuhaben, werde ich jede Methode in ein eigenes Thread packen, weil ich ja keine Fehler machen möchte.
    Und genau durch dieses Verhalten könnte meine Anwendung langsam und vielleicht gar nicht mehr laufen, weil das eine Thread auf das nächste wartet und beide verarbeiten nichts mehr.

    Oder anders gesagt, schieße nicht mit „Kanonen auf Spatzen“.

    Gruß, Doug_HH



  • Der gezeigte Code ist ein C# 3.0 Code. Wenn Du ihn einfach selbst mal zu übersetzen versucht hättest, wäre die Frage "Ich weiß nicht ob Du uns eine kleine Denksportaufgabe geben wolltest, oder den Code selber nicht übersetzt hast?" geklärt gewesen.

    DoEvents ist eine Krücke, die darauf hinweist, das eine Aufgabe eventuell in einem Thread besser aufgehoben wäre. In einer Single Thread Applikation geht man davon aus, das sich der Code jederzeit an einer definierbaren stelle aufhält. Das DoEvents bringt im gleichen Thread mehrere Ablaufszenarien ein, dessen Seiteneffekte die Gefahr darstellen.

    Du kannst Dir z.B. eine Änderung des Objektes einhandeln. Die Routine, die durch das DoEvents unterbrochen wurde, wird ein zweites mal aufgerufen und kann in undefiniertes Verhalten enden. Du kannst Probleme mit der Synchronisation Deiner Informationen erhalten. Bei Threads greift man hierfür auf Sperren zurück (z.B. Monitor). Ein Monitor wird Dir in einem single Thread nicht helfen.

    Race conditions werden im gleichen Thread plötzlich zu einem Problem.

    Und genau darin liegt die Gefahr des DoEvents und man sollte sich dessen immer bewusst sein.

    In einem GUI Thread mit einer CPU lastigen Aufgabe wird zudem ein DoEvents nicht reichen um die GUI vernünftig arbeiten zu lassen. Die Zeit, die dafür zur Verfügung steht, ist einfach zu kurz. Man hat also entweder eine GUI die nicht gescheit reagiert (z.B. ein Abbrechen Knopf) oder man packt so viele DoEvents in die Schleife, das die eigentliche Aufgabe nur langsam erledigt wird, weil stehts die Routine angehalten wird. (Hinzu kommt die Gefahr der Seiteneffekte.)

    Gehen wir noch weiter:
    Du schreibst ein Control, das ich auf die Form packe. Das Control setzt ein DoEvents ab. Ich würde bei der Verwendung des Controls nie damit Rechnen, das mein Code plötzlich an einer anderen Stelle weiterläuft. Ich wäre nicht vorbereitet, das ich Probleme mit Race conditions bekomme. Die Suche nach dem Problem würde sehr Aufwendig werden - Controls sind ja schließlich gut getestet so das man den Fehler dort nicht zuerst sucht. (Und man kennt es ja, beim Testen passiert nichts, erst wenn die Software beim Endanwender ist treten die Merkwürdigkeiten auf.)

    Ähnlich wie Globale Variablen sind DoEvents mit Vorsicht zu genießen, ermöglichen einen jedoch ein Problem mit geringen Aufwand zu lösen. Aber genauso wie globale Variablen sollte man diese nicht aus Bequemlichkeit einsetzen und immer erst die anderen Wege untersuchen. Die Seiteneffekte können unüberschaubar werden!

    Das so von Dir hochgelobte Threading hat nicht so einen guten Ruf.
    

    Die Erfahrung und Praxis wird Dir die dazu gehörende Einsicht bringen. Und das schöne dabei: Man wird trotzdem ab und an DoEvents verwenden, sich dann aber im Klaren sein was man Riskiert bzw. überschauen können ob es im Code ein Problem darstellt. Und genau das wird ein Anfänger nicht können. Solange er sich nicht mit Threading beschäftigt hat, werdn race conditions etwas unbekanntes sein. Er wird also nicht mal eine Idee haben was bei Problemen mit seinem Code passiert und relativ lange danach suchen.

    In einigen Fällen ist das eine Endlosschleife oder ein rekursiver Aufruf und die CPU wird von dieser Prozedur blockiert.
    

    Und genau das ist in der Regel ein sehr deutlicher Hinweis, das die Aufgabe in einen eigenen Thread gehört.
    (Ausnahmen bestätigen die Regel, Bequemlichkeit gehört jedoch nicht dazu.)

    [qoute]Und genau durch dieses Verhalten könnte meine Anwendung langsam und vielleicht gar nicht mehr laufen, weil das eine Thread auf das nächste wartet und beide verarbeiten nichts mehr. [/quote]

    Das kannst Du auch in einer Single Thread Applikation mit DoEvents erreichen.



  • Zum zweiten lässt sich der C# Code in der Tat so nicht übersetzen.
    Da zum einem ist dieses Zeichen => dem Compiler nicht bekannt, sowie die Variablen x,y sind unbekannt.
    Ich weiß nicht ob Du uns eine kleine Denksportaufgabe geben wolltest, oder den Code selber nicht übersetzt hast?
    Was ja auch nicht schlimm ist.

    => sind in C# Teil von Lambda- Expressions und benötigen den C# Compiler, der bei VS2008 dabei ist.
    (Auch die Variabeln x und y sind aus diesem Grund unbekannt.)
    Fazit: Der Code lässt sich scho übersetzen, man benötigt aber die richtigen Tools.

    Thema Threading:
    Multithreading bringt viele Probleme, aber auch viele Möglichkeiten. In diesem Fall hier, bin ich der Meinung, dass ein Thread angebracht wäre. In welcher Form lässt sich allerdings diskutieren ("nakter" Thread, Thread vom ThreadPool, Thread via Delegaten asynchron ausgeführt, etc.).

    Mir kommt Application.DoEvents wie ein "Murks" vor. Es prozessiert während der eigentlichen Arbeit (die auf dem anderen Thread erledigt wird) mittendrin wieder Windows Messages. habe das Gefühl, dass hier Verantwortlichkeiten (bezüglich zu erledigender Arbeit) vermischt werden.

    Hier noch ein Blog über Application.DoEvents:
    http://blogs.msdn.com/jfoscoding/archive/2005/08/06/448560.aspx

    Was wird denn in der while- Schleife gemacht? Polling?
    Villeicht kann der Thread- Ersteller besser / genauer umschreiben, was das Ziel mit dieser while- Schleife ist, ev. gibts auch andere Ansätze.

    Gruss Simon

    Edit: Knuddelbär war schneller *g*... und spricht mir aus der Seele... 🙂


Anmelden zum Antworten