Timer und while-Schleife asynchron laufen lassen



  • 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... 🙂



  • Mein Programm läuft mit Threads wesentlich schneller als mit Application::DoEvents().

    Was wird denn in der while- Schleife gemacht? Polling?

    Ich habe das While-Schleifen-Problem mehrfach. Einmal werden Fließkomma-Operationen durchgeführt, einmal Integer-Operationen, einmal schreibzugriffe auf die Festplatte und einmal Lesezugriffe. Nur die Lesezugriffe haben sich nicht beschleunigt.
    Das ganze Programm ist ein Benchmark-Programm.



  • Threads sind meiner Meinung nach für zwei Dinge:

    1. Paralellisieren von Prozessen (im Sinne von Arbeit erledigen)
    Hier macht es kein Sinn viel mehr Threads als CPU Kerne zu benutzen.
    2. Um IO Operationen (File IO, Netzwerk IO, etc.), die von Natur aus viel Zeit mit Warten verbringen,
    "paralell" warten lassen können (und damit andere Threads befähigen ihre Arbeit zu erledigen).

    In diesem Sinne ist deine Aussage logisch:

    Nur die Lesezugriffe haben sich nicht beschleunigt.

    Grüsse Simon



  • Hallo Doug_HH,

    welche Probleme DoEvents auslösen kann haben ja Knuddelbaer und vor allem auch der verlinkte Blogeintrag von simon.gysi schon gezeigt - DoEvents täuscht einfach eine Sicherheit vor, die man nicht hat.

    Der Aussage "Mit Kanonen auf spatzen geschossen" kann ich leider auch net ganz zustimmen. Klar, man muss bei Multithreading wissen was man tut und auf was man achten muss, aber das muss man überall. Ich hab oftmals das Gefühl sobald man Multithreading hört denke einige an was arg kompliziertes, dabei ist es in einfacher Ausführung eigentlich leicht Programme mit mehreren Threads zu schreiben. Sehe da nicht wo die Kanonen sind. Das es im Detail natürlich ganz schön verzwickt sein kann stimmt schon, aber das meist nur wenn Threads Daten teilen was hier nicht der Fall ist. Wenn sie für sich alleine laufen stellen sie keinerlei Stolpersteine dar.

    Das es C# 3.0 ist hätt ich vielleicht zuschreiben können, stimmt schon 🙂 Dachte des wäre durch Erwähnung der Lambda Ausdrücke klar gewesen.

    @Mr X
    Auch wenn du dich freust dass durch dein Programm wohl schneller läuft durch die Threads (was nicht zwangsläufig so sein muss), werden deine Ergebnisse wenn du Benchmarks damit veranstalten willst, nicht unbedingt genauer, da der Worker Thread der die Arbeit verrichtet ja auch gescheduled wird, und Zeit an andere Threads abgeben muss, und je nachdem wie du Zeit misst, kannst du mächtig daneben langen. Wenn du da nen Timer auf 2 Sekunden stellst und in deinem extra Thread läuft ne Schleife wo du am Ende gucken willst wie oft die durchgelaufen ist, kanns passieren das der Timer gestartet wird, der Thread startet, bekommt dann aber kaum Rechenzeit warum auch immer, der Timer stoppt den Thread, du schaust und siehst: ah ich hab 10000 Durchläufe, denkst dir du hast dein Ergebniss relativ genau, aber vielleicht war der Thread auch nur nen viertel der Zeit aktiv und das Ergebniss ist total verfälscht. Da muss man sich echt das Gesamtkonzept überlegen, wie man richtig Zeiten misst. In vielen Pseudo"bechmarks" die man irgendwo sieht um was auch immer zu vergleichen, wird das nämlich oft falsch gemacht.



  • Im Prinzip gilt dies für viele Benchmarking Programme.
    Man sollte natürlich während des Benchmarks nichts machen, sonst ist das Ergebnis ungenau/falsch. Dies gilt aber für alle Benchmarking-Programme, die ich bislang ausprobiert habe.



  • Hallo Talla,

    ich sehe es jetzt absolut und ohne Zweifel ein, dass man hier mit dem Threadcode arbeiten sollte und es vielleicht für andere Zwecke auch besser geeignet ist als DoEvents();

    Ich habe in der Tat nicht daran gedacht, dass DoEvents die Anwendung ausbremst, was aber logisch ist, da ja die Warteschlange abgerufen wird.
    Das gleiche passiert aber mit dem Threadcode auf einer „Ein-Prozessor-Maschine“.
    Dem sollte man sich auch bewusst sein.

    Visual C# 2005 von Andreas Kühnel schrieb:

    Eine Ein-Prozessor-Maschine vorausgesetzt, kann zu einem gegebenen Zeitpunkt nur ein Thread von der CPU bearbeitet werden. Stehen mehrere Threads derselben oder auch unterschiedlicher Anwendungen zur Ausführung in einer Warteschlange, erfolgt der Austausch der Threads in der CPU in sehr kleinen Zeitintervallen. Einem Anwender fällt das nicht auf – aus seiner Sicht erscheint es so, als würde die Ausführung gleichzeitig erfolgen.

    http://www.galileocomputing.de/openbook/visual_csharp/visual_csharp_11_000.htm

    Jetzt kurz mal zu C# 3.0.

    In erste Linie wäre es nett gewesen das zu sagen, dass das C# 3.0 ist, aber das ist nicht so schlimm.
    Teile der Syntax sind mir nämlich unbekannt.
    Wie ich sehe hat sich die Syntax auch ein wenig geändert.

    Oder wie soll ich die Parameter x,y verstehen?
    Oder sind das doch Variablen? Nur ich sehe keine Vereinbarung im Code.

    Oder => was macht der?

    timer.Elapsed += (x,y) => {
    

    Werde mir aber gleich mal mehr Info holen.

    Scheint sich aber was getan zuhaben in Richtung C# wie ich es durch kurze Infos im Netz mitbekommen habe.

    Gruß, Doug_HH



  • Doug_HH schrieb:

    Ich habe in der Tat nicht daran gedacht, dass DoEvents die Anwendung ausbremst, was aber logisch ist, da ja die Warteschlange abgerufen wird.
    Das gleiche passiert aber mit dem Threadcode auf einer „Ein-Prozessor-Maschine“.
    Dem sollte man sich auch bewusst sein.

    Es sei mal dahin gestellt, ob ein Switschen zwischen den Threads aufwendiger und Zeitintensiver ist als ein Spinup mit DoEvents, sollte der Vergleich 2 Threads == 1 Thread mit DoEvents auf einem Core noch mal überdacht werden.

    Ansonsten: Wer an dieser Stelle das Problem der DoEvents noch nicht erkannt hat und jetzt immer noch nach generellen Argumenten gegen Threads sucht (oder sogar mit 1 Core CPUs, 2 Threads mit Performancebedenken anrückt) möge sich die Problematik der DoEvents im stillen noch einmal selbst erörtern.


Anmelden zum Antworten