Быстрое добавление текста в текстовое поле

У меня есть BackgroundWorker нить, которая публикует сообщения, используя BeginInvoke на текстовое поле в графическом интерфейсе. Метод, write_debug_text, который отображает текст в текстовом поле использует AppendText а также пишет текст в Console,

Похоже, что BackgroundWorker пишет слишком быстро для write_debug_text поддерживать. Я установил точку останова на write_debug_text и придется долго ждать, пока его не ударили. Многие вызовы "BeginInvoke" происходят до достижения точки останова.

Я ищу отображение сообщений в реальном времени в пользовательском интерфейсе, очень похоже на System.Console в VS C# Express IDE.

Из поиска на ТА я так понимаю AppendText это более быстрый метод, и строки могут быть перераспределены.

Некоторые ответы предлагают использовать StringBuilder затем периодически записывать этот текст в текстовое поле. Но это требует добавления большего количества событий и таймеров; что я предпочел бы не делать (мое простое приложение становится все более и более сложным).

Как я могу написать в режиме реального времени в текстовое поле (и отображать его)?

Моя текущая идея - создать виджет, унаследованный от Textbox, который использует текстовую очередь и таймер.

Изменить 1: Пример кода

Вот фрагмент моего кода:

    private m_textbox;
    //...
    m_textbox.BeginInvoke(new write_debug_text_callback(this.write_debug_text),
                                      new object[] { debug_text });
    return;

private void write_debug_text(string text)
{
    string time_stamp_text = "";
    time_stamp_text = DateTime.Now.ToString() + "   ";
    time_stamp_text += text;
    m_textbox.AppendText(time_stamp_text);
    m_textbox.Update();
    System.Console.Write(time_stamp_text);
    return;
}

Я пытался изменить BeginInvoke в Invoke и мое приложение зависло. Когда я приостанавливаю / прерываю использование отладчика, указатель выполнения находится на вызове Invoke,

Кстати, у меня есть многолетний опыт работы с Java, C++ и C. Я на пятом месяце работы с C#.

6 ответов

Решение

Я воспользовался советом @Jeffre L. Whitledge и сократил количество распределений строк. Поскольку это закрытое приложение с фиксированным количеством строк, я кэшировал строки. Это произвело побочный эффект моей программы, выполняющейся значительно быстрее.

Одной из моих проблем по-прежнему является медленная реакция Windows на сообщения. Это можно увидеть, когда индикатор выполнения обновляется. Существует определенная задержка с момента отправки сообщения (например, добавления текста) и его выполнения.

Если отображается очень много сообщений, проблема, вероятно, связана с распределением памяти.

Давайте предположим, что каждое сообщение имеет длину 30 символов. Это будет (примерно) 60 байтов. Предположим также, что вы добавляете 10 сообщений в секунду. Тогда в первую секунду сгенерированные строки будут: 60 + 120 + 60 + 180 + 60 + 240 + 60 + 300 + ... + 60 + 600 = 3840 байтов. На второй секунде общее количество увеличивается до 13 740 байтов. На третьей секунде: 29 640 4-е: 51 540. ... 10-е: 308 940 байт.

Через 18 секунд достигается один мегабайт, и отображаемые строки имеют размер 11 КБ каждая.

На отметке в одну минуту мы находимся на 10 Мб выделенной строки. В две минуты 43 Мбайт посвящено этим сообщениям, и размеры строк увеличиваются на 71 кбайт каждое. Через три минуты размер сообщений превышает 100 КБ, и им выделяется около 100 МБ.

Вот почему StringBuilder так важен для построения длинных строк!

Но так как вы планируете отображать каждый промежуточный шаг, StringBuilder вам здесь не поможет. Этот графический интерфейс требует, чтобы вы генерировали метрическую загрузку строк.

Одним из возможных решений этой проблемы является отрыв передней части строк при вставке данных в заднюю часть. Вы по-прежнему будете выделять много памяти для строк, но сами отдельные строки будут иметь ограниченный размер, и, следовательно, во время выполнения будет гораздо проще найти для них место в памяти, и скорость выделения будет иди вниз.

Это должно снизить нагрузку на сборщик мусора:

private const int maxDisplayTextLength = 5000;

private void write_debug_text(string text)
{
    string time_stamp_text = "";
    time_stamp_text = DateTime.Now.ToString() + "   " + text;
    string previous = m_textbox.Text;
    if (previous.Length + time_stamp_text.Length > maxDisplayTextLength)
         m_textbox.Text = previous.Substring(0, maxDisplayTextLength - time_stamp_text.Length) + time_stamp_text;
    else
         m_textbox.Text = previous + time_stamp_text;
    m_textbox.Update();
    System.Console.Write(time_stamp_text);
    return;
} 

Может быть, попробуйте http://sourceforge.net/projects/fastlogconsole - имеет отличную производительность

Если вы хотите записи в реальном времени внутри текстового поля, почему бы не использовать синхронный Invoke вместо BeginInvoke какие очереди функция, которая будет вызвана позже?

Вы можете попробовать использовать RichTextBox вместо этого контролировать и не допустить обновления его пользовательского интерфейса, кроме как очень часто. Вроде как StringBuilder предложение, но немного проще. Посмотрите этот ТАК вопрос для примера, как это сделать,

Не использовать BeginInvoke, использование Invoke вместо. См. Приложение перестает отвечать при добавлении тысяч строк. Это не совсем та же проблема (он использует DataGridView, а не текстовое поле), но это то же самое. Ваш BackgroundWorker запускает целую кучу асинхронных задач. Тебе лучше заставить это делать по одному.

Другие вопросы по тегам