Fenster hängt bzw. reagiert langsam

Anfängerfragen zum Programmieren mit PureBasic.
Simon74
Beiträge: 60
Registriert: 04.05.2014 10:05

Fenster hängt bzw. reagiert langsam

Beitrag von Simon74 »

Hallo,

ich habe die bekannte HTTPProgress Prozedur verwendet, der zurückgelieferte String wird von einer anderen Prozedur() ausgewertet.
Aus dem GetXML werden dann vier Variablen gesucht und ausgewertet, diese 4 Werte stelle ich wiederrum in 4 Labelgadgets und 4 Progressbars visuell dar.
Der Auruf erfolgt jede Sekunde 2x (500ms).
Wenn diese Prozedurschleife läuft lässt sich das Programmfenster nur ungern verschieben bzw. reagiert etwas langsam.
An den Progressbar Gadgets liegt es nicht, auch ohne diese Darstellung reagiert das ganze verzögert.

Code: Alles auswählen

Procedure.s GetReceiverXmlAsString(URL.s) ;Holt die Daten(xml) vom Gerät ab, Timeout eingebaut
  Debug "GetReceiverXmlAsString()"
  Protected result, Download, Progress, Size, i=1
  
  Debug "GetReceiverXmlAsString(): <" + URL.s + ">"
  
  ;GetXML.s = ""
  Download = ReceiveHTTPMemory(URL.s, #PB_HTTP_Asynchronous)
  If Download
    Repeat
      Progress = HTTPProgress(Download)
      Select Progress
        Case #PB_Http_Success
          *XmlBuffer = FinishHTTP(Download)
          Debug "Download finished (size: " + MemorySize(*XmlBuffer) + ")"
          Size = MemorySize(*XmlBuffer)
          GetXML.s = PeekS(*XmlBuffer, Size, #PB_UTF8|#PB_ByteLength)
          ;Debug GetXML.s
          If FindString(LCase(GetXML), LCase("401 authentication required"))
            GetXML="401"
            Stop()
          EndIf
          ;FreeMemory(*XmlBuffer)
          Break 
          
        Case #PB_Http_Failed
          Debug "Download failed"
          sbText(L_BoxOffline)
          Stop()
          Break 
          
        Case #PB_Http_Aborted
          Debug "Download aborted"
          sbText(L_BoxOffline)
          Stop()
          Break 
          
        Default
          Debug "Current download: " + Progress
          If i = 2 * GetGadgetState(#spinTimeout) ; max. Timeout
            Debug "Download timeout"
            sbText(L_TimeoutMessage)
            Stop()
            Break 
          EndIf
          i+1
      EndSelect
      
      Delay(500) ; Don't stole the whole CPU
    ForEver
  Else
    Debug "Download error"
    sbText(L_BoxOffline)
    Stop()    
  EndIf
EndProcedure
In welche Richtung sollte ich suchen ?
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Fenster hängt bzw. reagiert langsam

Beitrag von STARGÅTE »

Wenn diese Prozedure synchron zur Event-Schleife aufgerufen wird, wird durch das Delay(500) dein Fenster für immer 500ms eingefroren.

Du kannst nun entweder das Delay reduzieren oder diese Prozedur asynchron aufrufen (z.B. mit Threads).
Wenn die Prozedur GetReceiverXmlAsString an sich eine "lange" Bearbeitungszeit hat, dann wird sie im synchron Modus auch dann noch das Fenster einfrieren, dann kommst du um eine Thread-Variante nicht umher.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Simon74
Beiträge: 60
Registriert: 04.05.2014 10:05

Re: Fenster hängt bzw. reagiert langsam

Beitrag von Simon74 »

Hallo Stargate,

vielen Dank für die Info,
die Aussage hatte ich schon vermutet/befürchtet.

In diesem Fall muss ich mich da mal einlesen wie das mit Threaded funktioniert, bis jetzt wurde ich noch nicht schlau daraus :)
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Fenster hängt bzw. reagiert langsam

Beitrag von NicTheQuick »

Nicht "Threaded", sondern "CreateThread()". "Threaded" wirst du vermutlich nicht benötigen. Aber du solltest in den Compiler-Einstellungen "Thread-safe" aktivieren.
Bild
Benutzeravatar
DarkSoul
Beiträge: 689
Registriert: 19.10.2006 12:51

Re: Fenster hängt bzw. reagiert langsam

Beitrag von DarkSoul »

Ist doch logisch. :mrgreen:

Die Prozedur muss erst durchlaufen werden, bevor wieder in die Hauptschleife zurückgekehrt wird, wo sich das WaitWindowEvent() befindet. WaitWindowEvent() enthält neben der für dich nützlichen Event-Abfrage andere Sachen, die für den flüssigen Betrieb im Multitasking notwendig sind.

Werden die Abstände zwischen den WaitWindowEvent()-Aufrufen zu groß, dann reagieren die Fenster träge bzw. gar nicht mehr.

Warum machst du das überhaupt in einer eigenen Schleife? Außerdem startest du derzeit mit jedem Aufruf den Download neu, was 2x die Sekunde nicht so dolle ist*. Ist das gewollt?

Starte doch den Download (der ist für sich bereits ein Thread, wenn er asynchron ist) dort, wo du jetzt diese Prozedur aufrufst, so dass die Hauptschleife danach weiterläuft. Die Rückgabewerte von mehreren parallel laufenden ReceiveHTTPMemory() kannst du z.B. in einem Array sammeln.

Dann kannst du dir eine Prozedur basteln, die einmal alle aktuell laufenden Downloads einmal checkt und entsprechend deine Progressbars aktualisierst. Diese Prozedur kannst du dann von der Hauptschleife aus alle Sekunde einmal aufrufen. Dann bleibt das Fenster auch bei laufenden Downloads flüssig und kannst dir die Threads erstmal sparen (und die 2 vollendeten Downloads je Sekunde auch).

*für solch häufige Kommunikation zwischen zwei Systemen ist die Network-Library oder WebSockets die bessere Wahl. Bedenke, dass bei HTTP immer zweimal übertragen wird: Einmal die Anfrage und einmal die Antwort darauf.

Das häufige Anfragen mittels HTTP an den Server (nach dem Motto: "is schon was da?" - nein!! - "is schon was da?" - nein!! - "is schon...") ist nicht sehr schön und verpulvert viel Traffik... Programme, die das so machen, haben meistens auch eine hakelige GUI, weil sie nach kürzester Zeit mit den vielen Antworten ihrer eigenen Anfragen überschwemmt werden. Also bitte: so nicht. <)

Eine Kompromisslösung, falls nur HTTP zur Verfügung steht und sonstwie nicht anders machbar, wäre eine GET-Anfrage zu stellen, die ein (möglichst) unendliches Timeout hat. Mit diesem "Ticket" hat der Server die Möglichkeit, dir jederzeit was schicken zu können, ohne auf deine nächste Anfrage warten zu müssen (indem er eine Antwort darauf liefert). Wenn der Server die Anfrage zur Antwort genutzt hat, wird direkt eine neue gestellt, damit der Server da wieder irgendwann drauf antwortet. Das hat jedoch auch wieder seine eigenen Schattenseiten....
Bild
Benutzeravatar
mhs
Beiträge: 224
Registriert: 11.01.2009 16:30
Wohnort: Graben
Kontaktdaten:

Re: Fenster hängt bzw. reagiert langsam

Beitrag von mhs »

Mir käme da sofort ein Timer in den Sinn.

Den Download asynchron starten, die Download ID in eine globale Variable und einen Timer alle 100/200ms starten. Im Timer wird dann die Download ID abgefragt, ggf. das Ergebnis des Downloads in GetXML geschrieben und die Gadgets für den Status angepasst. Wenn der Download fertig ist, wird der Timer beendet. So könntest du auf einen Thread verzichten und hättest trotzdem eine asynchrone Ausgabe ohne Verzögerung für die GUI.
Michael Hack

Michael Hack Software :: Softwareentwicklung | Webentwicklung | IT-Dienstleistungen
www.michaelhacksoftware.de :: www.mh-s.de :: www.michael-hack.de
Simon74
Beiträge: 60
Registriert: 04.05.2014 10:05

Re: Fenster hängt bzw. reagiert langsam

Beitrag von Simon74 »

Vielen Dank für die Antworten.
Timer hatte ich schon in Verwendung, jetzt habe ich auch CreateThread() erfolgreich umgesetzt, es macht nun "flüssig" was es soll, juhu. <)

Ich bekomme die Daten per http bzw. https, diese sollen/werden sofort als String und Progressbar angezeigt.
Die Software wird in der Regel nicht länger als 3 Minuten durchgehend laufen.
Folgendes wird mir vom Webservice geliefert:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<frontendstatus>
	<db>86 dB</db>
	<onr>86 %</onr>
	<ret>0</ret>
	<bar>79 %</bar>
</frontendstatus>
Gibt es betreffend Network-Library etwas fertiges für diesen Zweck ?
Benutzeravatar
DarkSoul
Beiträge: 689
Registriert: 19.10.2006 12:51

Re: Fenster hängt bzw. reagiert langsam

Beitrag von DarkSoul »

Die Network-Library verwendet TCP, um die technische Verbindung aufzubauen. Meines Wissens ist neben den HTTP-Requests, die du derzeit verwendest, darüber hinaus nichts enthalten.

Endet sozusagen bei OSI Schicht 4 und für alles darüber musst du selber sorgen.

Wie die Nachrichten innerhalb dieser Verbindung aussehen, musst du dir halt selber ausdenken. Im einfachsten Fall schickst du das XML zum Client und verlässt dich darauf, dass dieser es auch verarbeiten konnte. Ich würde das XML noch ein bisschen hübsch verpacken. Z.B. so:

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<message>
	<type>statusupdate</type>
	<timestamp>12345678</timestamp>
	<body>
		<frontendstatus>
		   <db>86 dB</db>
		   <onr>86 %</onr>
		   <ret>0</ret>
		   <bar>79 %</bar>
		</frontendstatus>
	</body>
</message>
Der äußére Rahmen ist immer der gleiche. Nur der Inhalt von "body" ist immer ein anderer. Mit "type" hast du die Möglichkeit, mehrere verschiedene Nachrichten über dieselbe Verbindung zu verschicken.

Oder du verwendest eine PB-Implementierung von WebSockets und arbeitest dann mit solchen.
Zuletzt geändert von DarkSoul am 07.02.2018 19:35, insgesamt 1-mal geändert.
Bild
Simon74
Beiträge: 60
Registriert: 04.05.2014 10:05

Re: Fenster hängt bzw. reagiert langsam

Beitrag von Simon74 »

Mein Event sieht nun so aus:
"Thread" (für auslesen und befüllen der Gadgets) wird nur gestartet wenn nicht mehr aktiv.
Der Timer ist auf 300ms eingestellt, dabei laufe ich eigentlich nie in einen laufenden Thread,
wenn ich die Abfragezeit verringere auf 100ms dann sehr wohl.
Kommt sicherlich darauf an ob LAN oder WLAN oder WAN.

Wenn jemand schlecht dabei wird bitte um Mitteilung. :)

Code: Alles auswählen

Procedure Window_Main_Events(event)
  Select event
    Case #PB_Event_Timer 
      Select EventTimer() 
        Case #ConnectTimer
          If IsThread(ThreadSignalLesen) = 0
            ThreadSignalLesen = CreateThread(@ReadSignalXmlAsString(), 1) 
            IsThread(ThreadSignalLesen)
            Debug "++++++++++++++++++++++++++++++++++"
            Debug FormatDate("S=%ss", Date())
            Debug "IsThread(ID): " + IsThread(ThreadSignalLesen)
          Else 
            Debug "----------------------------------"
            Debug "Thread läuft noch.."
            Debug FormatDate("S=%ss", Date())
            Debug "IsThread(ID): " + IsThread(ThreadSignalLesen)            
          EndIf
Zum beenden sende ich ein:

Code: Alles auswählen

CreateThread(@ReadSignalXmlAsString(), 0)
Der Wert wird in der Prozedur laufend abgefragt, wenn "0" dann:

Code: Alles auswählen

.....
  Else
    ; Jetzt musst du warten bis der letzte Wert reingekommen ist, darum 1 Sekunde lang Schleife fahren
    Debug "Thread beenden (ID): " + ThreadSignalLesen
    Protected i
    For i = 1 To 11
     Delay(100)
     Debug i
     If IsThread(ThreadSignalLesen) = 0
       Break
     EndIf
    Next 
    If IsThread(ThreadSignalLesen)
      Debug "Kill!"
      KillThread(ThreadSignalLesen)
    EndIf
    SetGadgetState(#ProgressBar_1, 0)
    SetGadgetState(#ProgressBar_2, 0)
    SetGadgetState(#ProgressBar_3, 0)
    SetGadgetState(#ProgressBar_4, 0)
Benutzeravatar
DarkSoul
Beiträge: 689
Registriert: 19.10.2006 12:51

Re: Fenster hängt bzw. reagiert langsam

Beitrag von DarkSoul »

Das mit dem Beenden funktioniert? :shock:

Code: Alles auswählen

CreateThread(@ReadSignalXmlAsString(), 0)
Das startet einen zweiten Thread, anstatt den ersten zu beenden. :wink:

Der zweite Parameter wird an den Thread übergeben.

Code: Alles auswählen

Procedure blaThread(*sollLaufen)
  Debug "Zufallszahlen generieren"
  While PeekI(*sollLaufen) <> 0
    Debug Random(10)
    Delay(100)
  Wend
  Debug "Thread beendet"
EndProcedure


threadSollLaufen.i = 1
Debug "Starte thread!"
CreateThread(@blaThread(), @threadSollLaufen) 
Delay(5000)
threadSollLaufen = 0
Debug "Thread, breche ab!"
Delay(5000)
Debug "Mainthread ist auch zu ende! Programmende!"
Die Semaphore habe ich mal weggelassen. :mrgreen:

Es ist relativ nützlich, diesen Parameter zur Übergabe eines Zeigers zu verwenden, um dem Thread einerseits Eingabedaten zu übergeben und andererseits eine Möglichkeit zu schaffen, dem Thread nachträglich Nachrichten senden zu können. Statt einen Zeiger auf einen einfachen Integer kannst du auch einen Zeiger auf eine Strukturvariable übergeben. Wie du den Parameter nutzt, ist dir überlassen.

In meinem Fall teilen sich der Thread und der Mainthread eine Integer-Variable. Wenn sie im Mainthread auf 0 gesetzt wird, kann der Thread das abfragen und entsprechend seine Schleife abbrechen.

Zudem würde ich KillThread() vermeiden, weil du damit den Thread gewaltsam abwürgst.
Bild
Antworten