Programm mit vielen AdlibRegister() aufrufen / Performance / NICHT Multithreading/Parallelprozessing

  • Moin,

    Vorweg: da mein Programm zur Zeit mehr als 6800 Zeilen hat poste ich es hier erst einmal nicht.

    In meinem Programm mit GUI nutze ich unter anderem per AdlibRegister()

    • Funktion1 die per _Timer_GetIdleTime() auf den Bildschirmschoner achtet und bei Bedarf einen MouseMove macht (alle 1000ms)
    • Funktion2 die auf Nachrichten eines Clients per TCP wartet - Client Connected, Client Disconnected und Nachrichten im JSON-Format (alle 50ms)
    • Funktion3 die prüft ob das Programm selbst richtig läuft (ob Funktion 4 seine Arbeit macht oder ob es einen nicht korrigierbaren Fehler gab) (alle 6000ms)
    • Funktion4 die Aufträge abarbeitet (alle 1000ms)

    Alles natürlich im OnEventMode
    Nun hatte ich es heute das z.B. eine Nachricht des Clients untergegangen ist (Funktion2 stand da noch auf 100ms)
    Ich habe alle Funktionen überarbeitet so das diese sich wieder schnellstmöglich beenden (per Return),
    bei Zeitkritischen Vorgängen halte ich sogar die ggf. störenden Funktionen an und starte diese hinterher wieder.

    Ich hatte schon mit den Gedanken gespielt so viel wie möglich in jeweils eigene Programme (und damit .exe) auszulagern ... aber dann wird es mit der Kommunikation kompliziert.
    Wichtig wäre mir das Funktion2 immer Nachrichten empfängt, die anderen Funktionen dürfen Ihre Arbeit dafür unterbrechen / pausieren.

    Ich habe die AdlibRegister() so verstanden das

    • Er die Funktion4 z.B, alle 1000ms aufruft
    • In der Zeit in der Funktion4 läuft findet kein anderer Aufruf (z.B. von Funktion2 die ja alle 50ms laufen soll)
    • Und wenn Funktion4 60 Sekunden braucht dann steht auch so lange alles andere

    Ich hab mir Labels in die GUI gebaut die jedesmal die Farbe wechseln wenn die zugehörige Funktion aufgerufen wird. Das hat das ganze eher langsamer gemacht :D aber das vorher geschriebene meine ich dabei erkannt zu haben.


    Nach ein wenig Googeln hatte ich diesen Beitrag aus dem Forum gefunden: Multithreading in autoit

    @Mars schrieb dort folgendes:


    Code
    Die Elegantere Methode ist der OnEventMode oder allgemeinere Callbacks. Damit lassen sich fast alle Skripte zufriedenstellend parallelisieren. Weiterhin ist geschicktes anwenden von Timern eine schöne Möglichkeit Funktionen parallel arbeiten zu lassen. (Dazu muss eine Funktion nur in einer (oder mehreren) Statischen Variable(n) ihren aktuellen Zustand speichern um später an der alten Stelle weitermachen zu können)


    Hätte mal jemand ein paar Beispiele für mich was mit

    • allgemeinere Callbacks
    • geschicktes anwenden von Timern

    gemeint ist?
    Gibt es einen Befehl mit dem ich in der Funktion sagen kann "Arbeite mal eben deine AdlibRegister() ab und dann mache hier in der nächsten Zeile weiter"?

    In Erwartung einer interessanten Diskussion,
    BLinz

    • Offizieller Beitrag

    Die AdlibRegister-Funktion muss schneller durchlaufen werden, als deren Aufruf, ansonsten hängt sich AutoIt irgendwann auf.
    Wenn Du zeitkritische Funktionen hast, dann ab damit in die Hauptschleife (beim OnEventMode). Dort können sie so lange brauchen, wie sie wollen.
    Etwas MultiThreading bekommst Du mit "_Timer_SetTimer". Die werden auch bei einem Sleep oder einer MsgBox oder sonstigen blockierenden GUI-Kram aufgerufen (Fenster bewegen).
    Hier mal ein kleines Testscript:

  • Ich arbeite grundsätzlich so, dass ich exakt weiß an welcher Stelle im Programm welcher Code läuft. Soll etwas Zeitlich wiederholt werden kann das im Regelfall auch nicht "überall" geschehen (z.B. bei einer grafischen Anwendung alle 16,66ms ein Redraw für 60FPS, da muss man genau wissen, ob das Bild überhaupt schon fertig gerendert ist). Adlib schiebt den auszuführenden Code immer irgendwo an eine nicht näher spezifizierte Stelle ein. Eine gute alternative sind Funktionen die mit einem internen Timer versehen sind, sodass sie auch nur alle xxxx Millisekunden tatsächlich durchlaufen werden.


    M

  • Die AdlibRegister-Funktion muss schneller durchlaufen werden, als deren Aufruf, ansonsten hängt sich AutoIt irgendwann auf.
    Wenn Du zeitkritische Funktionen hast, dann ab damit in die Hauptschleife (beim OnEventMode). Dort können sie so lange brauchen, wie sie wollen.
    Etwas MultiThreading bekommst Du mit "_Timer_SetTimer". Die werden auch bei einem Sleep oder einer MsgBox oder sonstigen blockierenden GUI-Kram aufgerufen (Fenster bewegen).
    Hier mal ein kleines Testscript:

    Danke für das Beispiel, den Befehl _Timer_SetTimer() habe ich bisher übersehen.

    Dein Beispiel hat aber einen "Fehler". Der Tooltip in der Hauptschleife fängt erst an zu laufen nachdem die GUI geschlossen wurde, sprich die Hauptschleife wird ignoriert und das Exit aus der Funktion greift nicht.
    So auf Anhieb habe ich nicht verstanden warum dem so ist - aber ich forsche noch.

  • Äh, da muss bei Dir etwas falsch laufen, denn das obige Script funktioniert bei mir einwandfrei.

    Mhh, auf den Windows 7 64bit Clients hier in der Firma ist das Problem genau wie beschrieben.
    Auf meinem Windows 10 Notebook gibt es genau einen Durchlauf (Tooltip mit "0") und dann bis zum schließen der GUI nichts weiter. Danach läuft die Schleife weiter

    AutoIt Version ist 3.3.14.2


    Wenn Du zeitkritische Funktionen hast, dann ab damit in die Hauptschleife (beim OnEventMode). Dort können sie so lange brauchen, wie sie wollen.

    Das habe ich jetzt mal genau so gemacht (Die Idee war schon wieder zu einfach für mich), ist gerade im Testlauf.

  • Oscars Skript läuft bei mir so wie es soll.

    Was Mars gepostet hat, würde ich auch eher empfehlen als Adlibregister().

    PS: 6800 Zeilen sind schon krass für AutoIt. :D

  • Bei mir auf Windows 10 X64 läuft das Script auch tadellos. Habe lediglich Zeile 22 ein wenig geändert...
    ToolTip('Count = ' & $i & '/ ' & '9999')

    Da gibt es aber auch eine UDF für... um Adlib mit Parametern aufrufen zu können... die dafür auch die _Timer... Funktionen benutzt.


    _AdlibEnhance

  • PS: 6800 Zeilen sind schon krass für AutoIt.

    Naja, 25% sind Kommentare - denn die sind wichtig!


    Danke erst einmal an euch alle , ich habe nun einige Ideen wie ich das ganze noch mal im Nachgang optimiere.
    Ich war ganz stolz alles in Funktionen und AdLigRegister() gepackt zu haben.
    Aber wie das nun mal so ist: 20% des Codes sind ist die eigentliche Funktion. und die restlichen 80% sind für eventuelle Probleme, Fehlerbeseitigungen, Prüfungen gegen Fehlbedienungen bzw. Konfigurationsfehler.
    Da wurde die ein oder andere Funktion eventuell zu träge.

    Das Programm macht - zur Zeit zuverlässig - was es soll. Aber ich werden es noch mal die Hauptschleife / Kernroutine optimieren und unnötigen Ballast zu entfernen.
    Da kann ich sicher einige doppelte Abfragen vermeiden bzw. andere vom Tempo her optimieren. Ich habe zum Beispiel die Unsitte mir die Konfiguration immer aus der Setup-GUI auszulesen (ob zum Beispiel ein CheckBox gesetzt ist oder nicht). Das funktioniert zwar, ich unterstelle mal das eine Variable abzufragen schneller ist als jedesmal GUICtrlRead zu machen.

    Und ich habe neulich mal wieder viel in der Hilfe gestöbert und einige "Aha, da gibt es ja was fertiges" oder "Mhh, damit wäre das ja viel einfacher" Momente gehabt.

    Die Methode von @Mars klingt auch sehr gut, da muss ich mir aber erst einen genauen Ablaufplan machen.

    Alles in allem klingt es danach das meine zukünftigen Programme wieder ein wenig besser als die alten werden.