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:

         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

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

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

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

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?
0
                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.

0
Azarien napisał(a)
                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.

0

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

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.

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.


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.

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

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

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.

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ść.

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.

0

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

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.

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.

1 użytkowników online, w tym zalogowanych: 0, gości: 1