Быстрое добавление текста в текстовое поле - PullRequest
1 голос
/ 30 марта 2011

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

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

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

Из поиска в SO я понимаю, что AppendText - это более быстрый метод для использования и что, возможно, придется перераспределять строки.

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

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

Моя текущая идея - создать виджет, унаследованный от 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 ]

1 голос
/ 13 сентября 2012

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

1 голос
/ 30 марта 2011

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

Предположим, что каждое сообщение имеет длину 30 символов.Это будет (примерно) 60 байтов.Предположим также, что вы добавляете 10 сообщений в секунду.Тогда в первую секунду сгенерированные строки будут: 60 + 120 + 60 + 180 + 60 + 240 + 60 + 300 + ... + 60 + 600 = 3840 байтов.На второй секунде общее количество увеличивается до 13 740 байтов.На третьей секунде: 29 6404-е место: 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;
} 
0 голосов
/ 15 мая 2011

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

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

0 голосов
/ 30 марта 2011

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

0 голосов
/ 30 марта 2011

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

0 голосов
/ 30 марта 2011

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

...