Forum: PC-Programmierung C# GUI blockiert


von golem (Gast)


Lesenswert?

Guten Morgen,

in einer Callback Funktion werden ankommende UDP Nachrichten 
verarbeitet.
Diese Daten werden in einem Steuerelement richTextBox dargestellt.
Nun ist es so, dass sehr viele Nachrichten in einer sehr kurzen Zeit 
ankommen können und die GUI blockieren. Wie könnte man dieses Problem 
lösen, so dass die GUI nicht blockiert wird?
1
private void _ReceiveDataEvent(UdpClient _UdpClient)
2
{
3
    Invoke((MethodInvoker)delegate
4
    {
5
        if(_UdpClient.ReceiveData[0] > 0)
6
        {
7
           richTextBox.Text += "Test";
8
        }
9
     });
10
}

von Pandur S. (jetztnicht)


Lesenswert?

indem man die cpu intensive Funktionalitaet in einen Thread auslagert.

Vor allem ist der Richtext+= das absolut Falscheste, das moeglich ist. 
Dabei wird neuer Speicher alloziert, um N byte groessser als vorher, 
alles bisherige kopiert, das neue angehaengt, und weiter geht's. Mach 
einen vordefiniert grossen Speicher, und fuell den vor dem Anzeigen.

Oder je nach moeglichkeiten der sprache : beginupdate.. aendern .. 
endupdate. Wobei aendern eine groessere aktion wie nur ein paar bytes 
ist.

: Bearbeitet durch User
von golem (Gast)


Lesenswert?

Danke für deinen Beitrag.

Ich habe nun in meiner Applikation einen Thread erstellt. Jetzt soll die 
empfangenen Daten in der Callback Funktion an den Thread weitergereicht 
werden. Wie wird sowas in C# realisiert?

von Kippenhannes (Gast)


Lesenswert?

Ich würde mir erst einmal überlegen, ob es sinnvoll ist, dass "sehr 
viele Nachrichten in einer sehr kurzen Zeit" überhaupt in "Echtzeit" 
angezeigt werden bzw. wem das nützen soll und ob es nicht reicht, 2 oder 
3 Mal pro Sekunde upzudaten. Es gibt natürlich verschiedene 
Lösungsansätze. Man könnte z.B. etwas wie Doppelbufferung verwenden: Die 
ankommenden Daten in einen Buffer 1 schreiben, beim Ablaufen des Timers 
zu Buffer 2 umschalten und diesen beschreiben während die Daten aus 
Buffer 1 in die Textbox (Schleife und AppendText) gesetzt werden. Beim 
nächsten Mal die Buffer tauschen.
Falls das Schreiben in die Textbox irgendwann trotzdem zu langsam ist 
und sich die Daten zu sehr aufstauen, evtl. mit eine Listbox o.ä. 
probieren.

von gehna (Gast)


Lesenswert?

nimm den Wireshark, der kann das.

von golem (Gast)


Lesenswert?

Ok mit Wireshark kann ich natürlich die ankommenden Daten anzeigen 
lassen.

Trotzdem möchte ich sehr gerne wiessen, wie man die ankommenden Daten in 
der UdpReceiveCallback Funktion einem Thread übergeben kann. Das müsste 
doch mit event / delegate funktionieren oder?

von Dennis R. (dennis_r93)


Lesenswert?

Wenn du wirklich die Daten in einem String immer hinten dran hängen 
willst solltest du dafür einen extra Thread machen. Der fertige String 
muss dann mit Invoke der Richtextbox zugeordnet werden.
Dieses multithreading ist aber sehr komplex und bietet viele 
Fehlerquellen. Du wirst dafür einen Tag brauchen.
Als Verbesserung kann ich dir dann noch einen Strinbuilder empfehlen. 
Der macht das deutlich performanter.
Und je länger der String wird, desdo länger dauert es. Eine 
Längenbegrenzung könnte dir helfen.

Gruß

von golem (Gast)


Lesenswert?

Ok ich werde das ganze so lassen. Ich sehe schon das ganze ist nicht so 
trivial.

Trotzdem vielen Dank.

von Max M. (maxmicr)


Lesenswert?

Wie wärs mit nem Backgroundworker, der den String zusammenbaut? Mit 
Invoke lässt du den String auf der textbox anzeigen. Es müsste nur eine 
Art Event vom UDP Stream geben, wenn er zu Ende ist, damit der 
Background Worker auch weiß, wann er aufhören muss. Bei jedem weiteren 
receive-event erzeugst du ein neues Backgroundworker Objekt, dem du die 
selben Events (doWork etc.) zuweist.

von S. R. (svenska)


Lesenswert?

Wenn das RTF-Steuerelement ewig lange zum Rendern braucht und dabei die 
GUI blockiert, dann musst du die Updatefrequenz reduzieren.

Statt
1
richTextBox.Text += "Test";
machst du dann ein
1
strBuffer += "Test";
wobei strBuffer ein globaler String ist.

In einem zweiten Thread führst du einfach folgende Endlosschleife aus:
1
while(1) {
2
  richTextBox.Text = strBuffer;
3
  System.Threading.Thread.Sleep(500);
4
}

Damit wird das RTF-Steuerelement nur ungefähr 2x pro Sekunde 
aktualisiert und sollte noch genug CPU-Zeit für Reaktionen übrig lassen.

von Sheeva P. (sheevaplug)


Lesenswert?

Oh D. schrieb:
> Vor allem ist der Richtext+= das absolut Falscheste, das moeglich ist.
> Dabei wird neuer Speicher alloziert, um N byte groessser als vorher,
> alles bisherige kopiert, das neue angehaengt, und weiter geht's. Mach
> einen vordefiniert grossen Speicher, und fuell den vor dem Anzeigen.

Ein Puffer, wie Du ihn vorschlägst, ist eine Alternative. Die andere 
ist, die einzelnen Strings in einer Liste zu speichern, und diese dann 
am Ende mit einer Funktion wie join() oä. zum Ergebnisstring 
zusammenzufügen. Der Vorteil: weil beim Zusammenfügen der Liste die 
Größe des Ziel-Strings ja schon bekannt ist, entfallen das ständige das 
realloc() und memcpy() der "+="-Lösung. Und der Vorteil gegenüber Deiner 
Lösung mit einem Puffer ist, daß der Speicherbedarf nicht größer ist als 
unbedingt nötig und man auch dann nicht in Verlegenheit kommt, wenn die 
ankommenden Daten dann doch einmal größer sind als der vorallokierte 
Puffer.

Ich habe mal eine Software zur Verwaltung der Sedcards einer 
Modelagentur optimiert. Da waren die Sedcard-Informationen und die 
Bilder der Models als Base64-codierte Strings in XML-Dateien 
gespeichert. Diese Dateien mit einem eventbasierten zu parsen, hat mit 
der "+="-Methode je nach Anzahl und Größe der Bilder bis zu einer Minute 
gedauert, mit der oben beschriebenen Array-Methode hingegen weniger als 
zwei Sekunden.

von Flitzenpiep (Gast)


Lesenswert?

S. R. schrieb:
>In einem zweiten Thread führst du einfach folgende Endlosschleife aus:
>
1
> while(1) {
2
>   richTextBox.Text = strBuffer;
3
>   System.Threading.Thread.Sleep(500);
4
> }
5
>

Wenn das in dieser Form in einem anderen Thread als dem 
"Haupt"(-GUI)-Thread stattfindet, kommt der große Einschlag ... Zudem 
ist es meist ungesund, ohne Synchronisierung von mehreren Threads aus 
auf die gleichen Daten zugreifen. Die oben genannten Ansätze mit 
Doppelbufferung/Timer bzw. Backgroundworker (oder inzwischen eher Tasks) 
sind da imho sinnvoller.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.