Почему добавление TextBox.Text во время цикла занимает больше памяти при каждой итерации? - PullRequest
82 голосов
/ 05 января 2012

Краткий вопрос

У меня есть цикл, который выполняется 180 000 раз.В конце каждой итерации предполагается добавлять результаты в TextBox, который обновляется в режиме реального времени.

Использование MyTextBox.Text += someValue приводит к тому, что приложение потребляет огромные объемы памяти, и ему не хватаетдоступная память после нескольких тысяч записей.

Есть ли более эффективный способ добавления текста в TextBox.Text 180 000 раз?

Редактировать Меня действительно не волнует результат этого конкретного случая,Тем не менее, я хочу знать, почему это похоже на проблему с памятью, и есть ли более эффективный способ добавления текста в TextBox.

Длинный (Оригинальный) вопрос

У меня есть небольшое приложение, которое читает список идентификационных номеров в файле CSV и генерирует отчет в формате PDF для каждого.После создания каждого файла PDF к ResultsTextBox.Text добавляется идентификационный номер отчета, который был обработан и успешно обработан. Процесс выполняется в фоновом потоке, поэтому ResultsTextBox обновляется в режиме реального времени по мере обработки элементов

В настоящее время я запускаю приложение с 180 000 идентификационных номеров, однако память, которую занимает приложениесо временем экспоненциально растет.Он начинается примерно на 90 КБ, но примерно на 3000 записей он занимает примерно 250 МБ, а на 4000 записей приложение занимает около 500 МБ памяти.

Если я закомментирую обновление для TextBox с результатами, память останется относительно постоянной, примерно на 90 КБ, поэтому я могу предположить, что запись ResultsText.Text += someValue - это то, что заставляет ее использовать память.

Мой вопрос, почему это?Как лучше добавить данные в TextBox.Text, который не потребляет память?

Мой код выглядит следующим образом:

try
{
    report.SetParameterValue("Id", id);

    report.ExportToDisk(ExportFormatType.PortableDocFormat,
        string.Format(@"{0}\{1}.pdf", new object[] { outputLocation, id}));

    // ResultsText.Text += string.Format("Exported {0}\r\n", id);
}
catch (Exception ex)
{
    ErrorsText.Text += string.Format("Failed to export {0}: {1}\r\n", 
        new object[] { id, ex.Message });
}

Стоит также упомянуть, что приложениеэто одноразовая вещь, и не имеет значения, что на создание всех отчетов уйдет несколько часов (или дней :)).Моя основная проблема заключается в том, что, если он достигнет предела системной памяти, он перестанет работать.

Я в порядке, оставив строку, обновляющую текстовое поле результатов, закомментированное для запуска этой вещи, но я хотел бы знать, еслиесть более эффективный способ памяти для добавления данных в TextBox.Text для будущих проектов.

Ответы [ 12 ]

0 голосов
/ 18 апреля 2017

Вы говорите, что память растет в геометрической прогрессии.Нет, это квадратичный рост , то есть полиномиальный рост, который не так драматичен, как экспоненциальный рост.

Вы создаете строки, содержащие следующее количество элементов:

1 + 2 + 3 + 4 + 5 ... + n = (n^2 + n) /2.

С n = 180,000 вы получаете общее выделение памяти для 16,200,090,000 items, то есть 16.2 billion items!Эта память не будет выделена сразу, но для GC (сборщика мусора) требуется много работы по очистке!

Также имейте в виду, что предыдущая строка (которая растет) должна быть скопирована вновая строка 179,999 раз.Общее количество скопированных байтов также идет с n^2!

Как и другие предлагали, используйте вместо него ListBox.Здесь вы можете добавлять новые строки без создания огромной строки.A StringBuild не помогает, так как вы также хотите отобразить промежуточные результаты.

0 голосов
/ 05 января 2012

Что-то, что не было упомянуто, - то, что, даже если вы выполняете операцию в фоновом потоке, обновление самого элемента UI ДОЛЖНО произойти в самом основном потоке (в любом случае в WinForms).

При обновлении текстового поля, есть ли у вас код, похожий на

if(textbox.dispatcher.checkAccess()){
    textbox.text += "whatever";
}else{
    textbox.dispatcher.invoke(...);
}

Если это так, то ваша фоновая операция определенно затруднена обновлением пользовательского интерфейса.

Я хотел бы предложить, чтобы ваш фоновый оператор использовал StringBuilder, как указано выше, но вместо обновления текстового поля каждый цикл, попробуйте обновлять его через регулярные промежутки времени, чтобы посмотреть, увеличивает ли он производительность для вас.ПРИМЕЧАНИЕ: не использовали WPF.

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