TextBox.Text += duże stringi

TextBox.Text += duże stringi
0

Witam.

Napisałem aplikację w której jest kilka wątków, które się razem komunikują ze sobą. Dla każdego wątku jest utworzony formularz składający się z textBoxa. Celem tego formularzu jest zbieranie wszystkich danych jakie otrzymał dany wątek( coś à la konsola diagnostyczna).

Scenriusz:

  1. Wątek otrzymał dane
  2. Watek wywołuje metode SetText dla formularza w celu aktualizacji danych w textBoxie.

W skrócie metoda wygląda tak:

Kopiuj
         public void setText(object obj)
        {

            if (InvokeRequired)
            {
                Utils.ObjectDelegate method = new Utils.ObjectDelegate(setText);
                Invoke(method, obj);
                return;
            }

            msg = (NetworkMessage)obj;

            if (msg.messageType == "Discovery")
            {

                if (this.NeighborDiscoveryFiltr == true)
                {

                    //sb - obiekt StringBuilder - zadeklarowany globalnie

                    sb.Append(DateTime.Now);
                    sb.Append("\t");
                    sb.Append(msg.messageType);
                    sb.Append(" ");
                    sb.Append(msg.senderTimeStampTicks);
                    sb.Append(" ");
                    sb.Append(msg.senderName);
                    sb.Append(" ");
                    sb.Append(msg.senderID);
                    sb.Append(" ");
                    sb.AppendLine();

                    this.textBox1.Text += sb.ToString();
                    sb.Clear();

                    //String text = msg.messageType + " " + msg.senderTimeStampTicks.ToString() + " " + msg.senderName.ToString() + " " + msg.senderID.ToString();
                    //this.textBox1.Text += DateTime.Now.ToString();
                    //this.textBox1.Text += "\t";
                    //this.textBox1.Text += text;
                    //this.textBox1.Text += "\r\n";



                }

            }

I tu pojawia się problem. Profiler, krzyczy, że operacja setText zajmuje prawie 50% czasu procesora, co raczej jest niedopuszczalne. Jakieś sugestie jak rozwiązać ten problem?

0
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:3 minuty
0

i nic mu ten wątek nie pomoże — zresztą StringBuilder nic tu nie pomoże, bo i tak w linii

Kopiuj
this.textBox1.Text += sb.ToString();

jest konkatenacja stringów, z czego jeden może być ogromny.

bogdans
Moderator
  • Rejestracja:prawie 17 lat
  • Ostatnio:prawie 5 lat
0
  1. Ogromny jest zapewne pierwszy argument operatora konkatenacji, a wtedy StringBuilder bardzo pomoże.
  2. Pisałam o metodzie append (w C# jej nie ma?), w Javie użycie append w polu tekstowym zmniejsza czas czterokrotnie, nie wpływa na użycie procesora (około 50%).
    Może warto zbierać napływające dane w StringBuilderze, a pole tekstowe uaktualniać co pewien czas?

To smutne, że głupcy są tak pewni siebie, a ludzie mądrzy - tak pełni wątpliwości. Bertrand Russell
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:3 minuty
0
Kopiuj
                this.textBox1.SelectionStart = this.textBox1.TextLength;
                this.textBox1.SelectionLength = 0;
                this.textBox1.SelectedText = sb.ToString();

znacznie przyspiesza dodawanie tekstu, ale zdaje mi się, że wymusza natychmiastowe odrysowanie kontrolki — przez co zysk jest mniejszy. sprawdź jak to wygląda pod profilerem.

edytowany 1x, ostatnio: Azarien
0
Azarien napisał(a)
Kopiuj
                this.textBox1.SelectionStart = this.textBox1.TextLength;
                this.textBox1.SelectionLength = 0;
                this.textBox1.SelectedText = sb.ToString();

znacznie przyspiesza dodawanie tekstu, ale zdaje mi się, że wymusza natychmiastowe odrysowanie kontrolki — przez co zysk jest mniejszy. sprawdź jak to wygląda pod profilerem.

Niestety, to jest jeszcze bardziej niewydajne, ale na szybko sprawdzałem więc mogę się mylić.

Wszystkie znaki na niebie i ziemi wskazują, że problemem jest ilość odmalowań kontrolki.
Wygląda na to, że: TextBox multiline to... kolekcja TextBox'ów "singleline" :)
Inaczej nie wiem jak wytłumaczyć fakt liniowej zależności czasu trwania aktualizacji pól kontrolki do ilości linii tekstu.
I niestety wygląda na to że, przy każdym dodaniu/usunięciu/konkatenacji odmalowywany jest każdy element TextBoxa, nawet jeżeli aktualnie nie jest widoczny.

I tu przychodzi na myśl takie rozwiązanie:

Utworzyć tablicę String o stałej ilości elementów(tyle ile linii na raz widocznych w textBoxie) - i wykorzystać ją jako bufor pomiędzy StringBuilderem przechowującym całość danych a danymi aktualnie widocznymi w TextBox'ie.

Sprawdzę to dzisiaj i dam znać jaki jest tego efekt.

PS. To tylko taka luźna refleksja, nie wiem czy to co napisałem wyżej o TextBoxie mulitline jest prawdziwe

1

textBox.Text jest typu String, Stringi są immutable więc każda zmiana (np. dopisanie litery na końcu) oznacza przepisanie całości w nowe miejsce. Naprawdę musisz aktualizować textBox po każdej zmianie treści? Rozważ takie rozwiązanie: dopisujesz nowe wiadomości do bufora typu StringBuilder, prócz tego działa timer, który co pewien czas przepisuje zawartość bufora do pola tekstowego, odświeża pole i czyści bufor.

siararadek
  • Rejestracja:ponad 17 lat
  • Ostatnio:prawie 9 lat
0

Ale textbox też ma Append, textbox.Append(tekst) zamiast textbox.Text+= tekst ?

bogdans
Moderator
  • Rejestracja:prawie 17 lat
  • Ostatnio:prawie 5 lat
0

A to Append() nie przepisuje tekstu w nowe miejsce? Gdyby Append() nie przepisywało, to String nie byłby immutable. Wystarczyłoby stworzyć niewyświetlany textBox.
W Javie zamiana getText()+=s na append() skraca czas wykonania czterokrotnie. Natomiast zamiana konkatenacji Stringów na append() w StringBuilderze skraca czas wykonania kilkadziesiąt tysięcy razy.


To smutne, że głupcy są tak pewni siebie, a ludzie mądrzy - tak pełni wątpliwości. Bertrand Russell
edytowany 1x, ostatnio: bogdans
0

Tu problem nie jest appendowanie Stringa, czy appendowanie TextBox.AppendText. A raczej nadmierne odrysowywanie kontrolki przy ustawianiu TextBox.Text. Wygląda na to, że kontrolka odrysowuje wszystkie linie, nawet jeśli nie są one aktualnie wyświetlane.

Kopiuj

private Stopwatch watchSBAppendTime;
private Stopwatch watchTextBoxTime;

while(process == true)
{
	watchSBAppendTime.Start();

	sb.Append(DateTime.Now);
	sb.Append("\t");
	sb.Append(msg.messageType);
	sb.Append(" ");
	sb.Append(msg.senderTimeStampTicks);
	sb.Append(" ");
	sb.Append(msg.senderTime);
	sb.Append(" ");
	sb.Append(msg.senderName);
	sb.Append(" ");
	sb.Append(msg.senderId);
	sb.Append(" ");
	sb.AppendLine();

	watchSBAppendTime.Stop();

        watchTextBoxTime.Start();

	this.textBox1.AppendText(sb.ToString());
	//this.textBox1.Text = sb.toString();

	//this.textBox1.SelectionStart = this.textBox1.TextLength;
	//this.textBox1.SelectionLength = 0;
	//this.textBox1.SelectedText = sb.ToString();

	watchTextBoxTime.Stop();

	sb2.Clear();


	watchTextBoxTime.Reset();
	watchSBAppendTime.Reset();

}

I wynik (ticks):

Append StringBuilderTime: 61 - TextBoxTextTime - 6551
Append StringBuilderTime : 60 - TextBoxTextTime - 9724
Append StringBuilderTime : 150 - TextBoxTextTime - 28240
Append StringBuilderTime : 57 - TextBoxTextTime - 12623
Append StringBuilderTime : 74 - TextBoxTextTime - 21953
Append StringBuilderTime : 60 - TextBoxTextTime - 19082
Append StringBuilderTime : 83 - TextBoxTextTime - 19628
Append StringBuilderTime : 77 - TextBoxTextTime - 20570
Append StringBuilderTime : 76 - TextBoxTextTime - 29843
Append StringBuilderTime : 57 - TextBoxTextTime - 25638
Append StringBuilderTime : 74 - TextBoxTextTime - 35597
Append StringBuilderTime : 71 - TextBoxTextTime - 33327
Append StringBuilderTime : 65 - TextBoxTextTime - 31355
Append StringBuilderTime : 63 - TextBoxTextTime - 33462
Append StringBuilderTime : 62 - TextBoxTextTime - 42418
Append StringBuilderTime : 59 - TextBoxTextTime - 30293
Append StringBuilderTime : 59 - TextBoxTextTime - 32365
Append StringBuilderTime : 62 - TextBoxTextTime - 31323
Append StringBuilderTime : 85 - TextBoxTextTime - 32847
Append StringBuilderTime : 58 - TextBoxTextTime - 29941
Append StringBuilderTime : 63 - TextBoxTextTime - 55623
Append StringBuilderTime : 62 - TextBoxTextTime - 38590
Append StringBuilderTime : 59 - TextBoxTextTime - 41562
Append StringBuilderTime : 64 - TextBoxTextTime - 34767
Append StringBuilderTime : 87 - TextBoxTextTime - 40094
Append StringBuilderTime : 91 - TextBoxTextTime - 42583

Gdyby problemem było appendowanie Stringa to różnica w czasie wykonania się była by minimalna przy 1, 2, 3, 4 ... 10000 i więcej linijkach tekstu w TextBoxie - a tak nie jest.

bogdans
Moderator
  • Rejestracja:prawie 17 lat
  • Ostatnio:prawie 5 lat
0

prócz tego działa timer, który co pewien czas przepisuje zawartość bufora do pola tekstowego, odświeża pole i czyści bufor


To smutne, że głupcy są tak pewni siebie, a ludzie mądrzy - tak pełni wątpliwości. Bertrand Russell
UB
  • Rejestracja:około 17 lat
  • Ostatnio:ponad 7 lat
0
qq napisał(a)

Utworzyć tablicę String o stałej ilości elementów(tyle ile linii na raz widocznych w textBoxie) - i wykorzystać ją jako bufor pomiędzy StringBuilderem przechowującym całość danych a danymi aktualnie widocznymi w TextBox'ie.

może nie tyle ile jest widocznych, ale jakąś stałą liczbę.
takie coś ma miejsce chociażby w konsoli windows, która też nie przechowuje wszystkich linii w nieskończoność.
tylko wtedy pojawia się pytanie czy przechowywać w stringbuilderze całość tekstu, czy też tylko ilość linii? można np. przechowywać całość i dać możliwość userowi, na jego wyraźne żądanie, wyświetlenie całości, a można w ogóle nie dać mu wyboru, jak nie pamięta, to jego problem :P

adf88
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 12 lat
0

Spróbuj użyć RichTextBox, może jest bardziej zoptymalizowany pod względem obsłużenia większej ilości tekstu niż zwykły TextBox.
Możesz też spróbować podpiąć List<String> do DataGridView.

edytowany 4x, ostatnio: adf88
adf88
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 12 lat
0
ob napisał(a)

textBox.Text jest typu String, Stringi są immutable więc każda zmiana (np. dopisanie litery na końcu) oznacza przepisanie całości w nowe miejsce

bogdans napisał(a)

A to Append() nie przepisuje tekstu w nowe miejsce? Gdyby Append() nie przepisywało, to String nie byłby immutable.

AppendString niekoniecznie musi tworzyć od razu nowy String. Wewnętrznie klasa może działać na jakimś buforze a'la StringBuilder natomiast cały tekst w postaci obiektu String może być tworzony dopiero na żądanie np. gdy wywołamy getter własności Text.

Oczywiście to jest tylko możliwość.

edytowany 3x, ostatnio: adf88
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:3 minuty
0

wydaje mi się, że TextBox trzyma cały tekst w postaci pojedynczych linii - jakiejś tablicy czy coś.
właściwość Text jest tylko po to, by móc przypisać wszystkie linie na raz - ale i tak podczas przypisywania tekst jest od razu dzielony na linie.
Więc to nie „cały tekst” jest tworzony, bo jest on tak naprawdę niepotrzebny.

edytowany 1x, ostatnio: Azarien
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:2 dni
  • Lokalizacja:Wrocław
0

TextBox.Text korzysta z TextBoxBase.Text, a ten z Control.Text, które to jest zwykłym stringiem.

Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:3 minuty
0

tya, ale czy jest ten .Text używany do rysowania?
bo jak większa część Windows Forms, jest to zapewne tylko wrapper na gotową kontrolkę WinAPI — a jak ta działa, nie wiem.

UB
  • Rejestracja:około 17 lat
  • Ostatnio:ponad 7 lat
0

No na pewno jest to wrapper, czytałem gdzieś o tym jak szukałem o ustawianiu własnego tła w textboxie.
Na szczęście w WPF (btw. jak widzicie WTF to też wam się kojarzy z WPF?) da się ustawiać już swoje tło.

Azarien
odwrotnie: WPF kojarzy mi się z WTF. zarówno pod względem skrótu, jak i VS-owym edytorem WTF-owych form i całym XAML-em.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.