Delphi-PRAXiS
Seite 3 von 6     123 45     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Thread.Queue, Zeitmessung, Thread hängt angeblich (https://www.delphipraxis.net/217196-thread-queue-zeitmessung-thread-haengt-angeblich.html)

Uwe Raabe 20. Mai 2025 16:55

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
Zitat:

Zitat von himitsu (Beitrag 1548760)
Beim Queue ist die Zeitmessung aber auch bissl nutzlos, da es dort nur das Eintragen in die Liste misst, aber nicht die Funktion.

Wenn ich das richtig verstanden habe, ist aber genau das das Problem:
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1548710)

Delphi-Quellcode:
TC := GetTickCount;

//Synchronize(MeineFunktion);
Queue(Nil, MeineFunktion);

TC2 := GetTickCount - TC;
//Protokollierung von TC2...
TC2 ist teilweise 500ms.
Unter Synchronize verstehe ich das ja, aber wieso bei Queue auch?


Kas Ob. 20. Mai 2025 17:14

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
There is good advices above, and here what i see
1) according to screenshot, your main thread is spending in kernel time 4 times more than user time, that way more than usual or accepted and this is the cause.
2) saying you see red in CPU usage, meaning the threads are spedning too much time and also invoking system calls (not system functions), and/or too much IO operations.
3) This bring me to ask how many Threads are you creating and destroying per second ?
4) How many IO operation are you issueing per second ?
5) you mentioned UDP above, i might missed something, but if this is server, are you polling the status like that code above? i mean perform select over 250ms then loop back ? how many threads are doing this?
6) I have problem with this "if WaitForsingleObject(Something, 250, xxxx) = WAIT_OBJECT_0 do" in what thread it being used, also one thread or more? like 64 ?
7) Are these TC1,TC2,TC3,TCx... local variables ? i mean all, or some of them are accessible and writable by multiple thread, when i see stuff like this "TC2 := GetTickCount - TC;" this should not be used out side the scope of this thread, example how this can go wrong, two threads calling GetTickCount each within interval supposed to be 250ms, you assume the difference between their result will be within 250ms, in real life the difference can be 5 seconds (5000ms)! or even more, you have no control over this.
8) also as (7) and even more important if one thread performed an action and in the last stage updated some checkpoint timer lets say grabbed GetTickCount, then signaled for finish, if you are signaling before grabbing the time, then this thread might block for more time the next, then update the time indicator send the next thread in waiting state because code like this "TC2 := GetTickCount - TC;" if TC is not local and only limited to this thread then it easily can be negative value, be extra careful here.


Now to most important quesitons,
what are the operations these threads are doing ?
writing files or sending/recv packets ?
and why there is this very short period 250ms anywhere in your code ? this is not a joke and easily can go from speed enhancer to speed killer.

no matter what is the operation, then you must remove the dependency on time and switch to operation completion, in other words the fact you depend on GetTickCount to time or singal your threads to work, indicate critical problem in and design shortcoming.

himitsu 20. Mai 2025 18:02

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1548761)
Wenn ich das richtig verstanden habe, ist aber genau das das Problem:

Bei Diesem sollte der Lock aber nicht lange dauern und es nahezu sofort zurückkehren.

Einzig mir bekannte Ausnahme ist eben, wenn Queue aus dem Hauptthread aufgerufen wird, wodurch es fast wie Synchronize wirkt (die Funktion wird direkt aufgerufen, ohne in der Queue zu landen).
Oder wenn jemand es geschafft hat den Speicher zu schrotten (BufferOverflow, mistige Zeiger und Dergleichen), aber dann hat man eh andere Probleme, als nur dieses Hängen.

AJ_Oldendorf 21. Mai 2025 05:47

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
Guten Morgen,
also nochmal grob erklärt und zusammengefasst:
Es sind ca 35 UDP Teilnehmer an der Anwendung angemeldet (die Anwendung ist ein UDP Server und empfängt die Daten im OnUDPRead Event der Indy Komponente). Jeder UDP Teilnehmer ist ein eigener Thread da dieser losgelöst kommunizieren kann von den anderen.
Meine Threadinstanz, wo ich das Problem mit der Zeitmessung habe, bekommt die Daten von dem jeweiligen UDP Thread geschickt via "PostThreadMessage".

Diese Threadinstanz (existiert also auch 35x, da 35 UDP Teilnehmer online sind), empfängt die Daten in ihrer ThreadQueue grob durch folgenden Aufruf:

Delphi-Quellcode:
while not Terminated do
begin

  //hier erfolgt eine Signalisierung über WaitForSingleObject und bei Timeout (=250ms), erfolgt der zyklische Aufruf unten
  if WaitForsingleObject(Irgendwas, 250, xxxx) = WAIT_OBJECT_0 do
  begin

  end;

  //zyklischer Aufruf
  var sw := TStopwatch.StartNew;
  var TC : DWORD;
  TC := GetTickCount;

  //Synchronize(MeineFunktion);
  //Queue(Nil, MeineFunktion);
  //ForceQueue(Nil, MeineFunktion);

  t1 := sw.ElapsedMilliseconds;
  //Protokollierung von t1 ...

  TC2 := GetTickCount - TC;
  //Protokollierung von TC2 ...
end;
Die Protokollierung der Zeitmessung sowohl von t1 als auch TC2, schlägt zu. Egal welcher der 3 Aufrufe genutzt wird.
Eine Prüfung vorher auf
Delphi-Quellcode:
if (MainThreadID <> GetCurrentThreadID) then
zeigt auch, dass es nicht der VCL Thread ist als Aufrufer.
Die Protokollierung innerhalb der Funktion "MeineFunktion" schlägt nicht zu.

Ich muss weitere Untersuchungen anstellen, da auch ein Ausklammern der Funktion "MeineFunktion" dazu führt, dass die VCL hängt. Synchronize Aufrufe habe ich keine mehr im Programm. Ich muss prüfen, wer die VCL blockiert. Eurekalog habe ich schon mal installiert aber bei HangDetection wurde mir nichts angezeigt bzw. wann wird da wo was angezeigt? In dem Moment wo die Anwendung hängt oder beim Beenden?

AJ_Oldendorf 21. Mai 2025 07:14

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
Edit:
Habe mir das OnIdle Event der Anwendung mal protokolliert und das kommt 7s nicht, genau in dem Zeitraum, wo die VCL auch hängt

QuickAndDirty 21. Mai 2025 08:37

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
Was passiert wenn du mehr "PostThreadMessage" ausführst als in den 35 threads und dem Protocoll thread und dem Hauptthread ausgefürht werden können?
Gibt es ein Maximum and Queue size? Oder ist die Queue irgendwann Greedy und will abgearbeitet werden bevor neue Messages auflaufen?
Gibt es da einen Mechanismus in der Art der zum tragen kommen könnte?

Was wenn du von Threads auf Tasks(ITask) umsteigst, oder ist da kein effizenz Gewinn möglich da du eh schon mit nem Threadpools arbeitest?

Kas Ob. 21. Mai 2025 09:00

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
Now i am more confused than before, sorry.

You did not explain what is going on, as you still abusing Indy and its threads !

1) Your server is using multithread to handle UDP connections, that is fine and is in fact the right way, BUT as you did again miss the explaining what exactly and how you handle these received UDP packets, i will assume you are handling them all in the main thread and enforcing this by these
Delphi-Quellcode:
  //Synchronize(MeineFunktion);
  //Queue(Nil, MeineFunktion);
  //ForceQueue(Nil, MeineFunktion);
Congratulations, you defeated the whole purpose of using multithread server to handle connections, and you are better to switch to event based server and handle everything in the main thread.
The whole approach of using or receiving packets and handling in the main thread, is to not block packet handling and absolve the main thread to handle UI.
But this is based on what i see from the code you pasted, the same code without more details, like where the UDP packers read is happening.

2) What on earth this WaitForsingleObject is used for ?, what the idea of it ? purpose ? and it is inside the thread execution loop ? that can't be more wrong on so many level, it is like you have one wheel but only interacting with it with stick(s).

3) You confirming the TC, yet i still see TC2, and don't see where these variables declared are they all local to this thread ? how are you logging ?

4) you mentioned PostThreadMessage, how this fit in all of this, you either use PostThreadMessage or one of these
Delphi-Quellcode:
  //Synchronize(MeineFunktion);
  //Queue(Nil, MeineFunktion);
  //ForceQueue(Nil, MeineFunktion);
never together, well you can, but nothing good is coming out of it.

5) OnIdle is taking 7 seconds, is clear evidence of you killing the main thread, now you said the CPU is not high or no CPU usage, so let me explain how modern CPU(s) work, one thread will run on one core at any moment, so main thread is hugged or and on full job, then one core will go 100%, if you look on you overall CPUusage and it is 4 cores, then it will show 25%, if you CPU 8 then it will show %12 ....
So look again how much your main thread is utilizing.

6) THe fact Process Explorer showing too much red, and from your screenshot, i pointed that kernel time is multiple over user time, is clear that main thread is put to handle IO or something has to do with kernel operation, what is it ?

Here an example on how to handle UDP, it is this simple and don't complicate things, https://stackoverflow.com/questions/...simple-strings

Know this : You can receive thousands of packets of hundreds clients per seconds on main thread and it will not glitch for fraction of seconds, as you don't handle or process these packets just receive, so either receive everything in main thread then signal few threads to process, or receive in background thread and handle them there, never in main thread.

Main thread is not super thread or faster than any other threads, on contrary it is burdened with UI handling, so less job for it,

ps :
7) are you logging in main thread ? how many operation per second ? also how many packets you do receive per seconds ?

AJ_Oldendorf 21. Mai 2025 09:56

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
PostThreadMessage wird sicherlich noch an anderen Stellen aufgerufen, da dies ja eine Nachricht an ein bestimmten Thread ist (mit Angabe des ThreadHandles wird PostThreadMessage aufgerufen).

ITask habe ich noch nie benutzt und wüsste jetzt auch nicht, warum man das umstellen sollte?! Vor- und Nachteile?

@Kas Ob.
Ich versuche nur auf ein Bruchteil zu antworten, da meiner Meinung nach Fragen dabei sind, die nichts damit zu tun haben.

Die IdUDPServer Komponente ruft das OnUDPRead Event auf. Dort werden die Nachrichten an die entsprechenden Clients-Threads weitergeleitet.
Der Aufruf des OnUDPRead Events erfolgt NICHT in der VCL.
Geprüft durch Abfrage
Delphi-Quellcode:
if (MainThreadID <> GetCurrentThreadID) then
Die Threads empfangen die Nachrichten und machen damit etwas.
Auf das erfolgt nicht in der VCL.

Ich sage auch nicht, dass es am UDP hängt! Ich bekomme es nur damit nachgestellt. Es hängt definitiv die VCL (siehe IdleEvent). Eventuell ist auch etwas anderes Schuld. Synchronize Aufrufe sind KEINE im Programm.
CPU Auslastung vom MainThread ist laut Prozessexplorer 0.05, während die Anwendung hängt!
Mein Rechner hat 24 Kerne, eine volle Auslastung wären demnach 4%, welche ich aber NICHT habe.

Kleine Korrektur, es ist nicht "WaitForsingleObject" sondern "MsgWaitForMultipleObjects".
Wenn der Returnwert = WAIT_OBJECT_0 ist, wird die Thread-Queue abgearbeitet mit
Delphi-Quellcode:
while PeekMessage(MessageData, 0, 0, 0, PM_REMOVE) do
Ja, alle Variablen für die Zeitmessung sind lokal. Ich hatte TC2 vergessen mit in dem Codeschnipsel zu schreiben. Ist ja nur ein Ausschnitt des Codes und da hatte ich TC2 übersehen.

himitsu 21. Mai 2025 10:30

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
Die ThreadQueue hat ein Limit (keine Ahnung wie groß)
und wenn voll, dann wird beim PostThreadMessage/PostMessage/SendMessage die Eintragung verworfen und diese Funktionen geben einen Fehlercode zurück.

Jeder Thread hat seine eigene Queue, welche beim ersten Zugriff erzeugt wird.
Statt einer GDI-Queue könnte man z.B. eine TThreadList verwenden. (bzw. es gibt auch irgendwo threadsichere TQueueIrgendwas und TStackIrgendwas im Delphi)

AJ_Oldendorf 21. Mai 2025 10:51

AW: Thread.Queue, Zeitmessung, Thread hängt angeblich
 
PostThreadMessage habe ich immer auf das Result abgefragt und dies ist nie False, also immer erfolgreich gewesen.

Hier auch nochmal als Beweis eine Protokollierung im OnUDPRead Event:

AThread.ThreadID: 30856
MainThreadID: 32612
GetCurrentThreadID: 30856

Also wird es nicht im Hauptthread empfangen und verarbeitet


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:54 Uhr.
Seite 3 von 6     123 45     Letzte »    

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz