Многопоточность - является ли пул потоков хорошим выбором? - PullRequest
1 голос
/ 26 января 2012

У меня есть приложение на c # (. Net 3.5), которое импортирует тысячи файлов. Прямо сейчас я создаю фоновый рабочий для каждого файла. Он работает хорошо до определенного предела, а затем приложение умирает с исключением системы из памяти. Я предполагаю, что это происходит из-за большого количества потоков. Является ли threadpool хорошим решением для этой ситуации?

Исключение составляет:

    System.OutOfMemoryException | Exception of type 'System.OutOfMemoryException' was thrown. 
    at System.Data.RBTree`1.TreePage..ctor(Int32 size)
    at System.Data.RBTree`1.AllocPage(Int32 size)
    at System.Data.RBTree`1.InitTree()
    at System.Data.Index.InitRecords(IFilter filter)
    at System.Data.Index..ctor(DataTable table, Int32[] ndexDesc, IndexField[] indexFields,           
    Comparison`1 comparison, DataViewRowState recordStates, IFilter rowFilter)
    at System.Data.DataTable.GetIndex(IndexField[] indexDesc, DataViewRowState recordStates, IFilter 
    rowFilter)
    at System.Data.DataColumn.get_SortIndex()
    at System.Data.DataColumn.IsNotAllowDBNullViolated()
    at System.Data.DataTable.EnableConstraints()
    at System.Data.DataTable.set_EnforceConstraints(Boolean value)
    at System.Data.DataTable.EndLoadData()
    at System.Data.Common.DataAdapter.FillFromReader(DataSet dataset, DataTable datatable, String    
    srcTable, DataReaderContainer dataReader, Int32 startRecord, Int32 maxRecords, DataColumn    
    parentChapterColumn, Object parentChapterValue)
    at System.Data.Common.DataAdapter.Fill(DataTable[] dataTables, IDataReader dataReader, Int32 
    startRecord, Int32 maxRecords)
    at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 
    startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
    at System.Data.Common.DbDataAdapter.Fill(DataTable[] dataTables, Int32 startRecord, Int32  
    maxRecords, IDbCommand command, CommandBehavior behavior)
    at System.Data.Common.DbDataAdapter.Fill(DataTable dataTable)
    at Dms.Data.Adapters.DataTableAdapterBase`2.FillByCommand(TTbl table, DbCommand command)

Ответы [ 4 ]

4 голосов
/ 26 января 2012

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

Использование ThreadPool может помочь, так как это может дать вам средство ограничения обработки.Однако, если вы импортируете и обрабатываете «тысячи файлов», подходящим средством может быть создание конвейера для обработки, а затем заполнение конвейера (или определенного числа из них) вашими файлами.Это позволит вам контролировать количество параллелизма и предотвратить одновременную обработку слишком большого количества отдельных файлов.Это может держать ваши требования к памяти и обработке на более разумном уровне.


Редактировать:

Поскольку вы (сейчас) упомянули, что используете C # ... BackgroundWorker фактически делаетиспользуйте ThreadPool.Переключение на использование пула потоков напрямую может быть хорошей идеей, но, вероятно, не решит проблему полностью.Возможно, вы захотите использовать что-то вроде BlockingCollection<T> для настройки очереди производителя / потребителя.Затем вы можете иметь 1 или более потоков, которые «потребляют» файлы и обрабатывают их, и просто добавить все файлы в BlockingCollection<T>.Это даст вам контроль над тем, сколько файлов обрабатывается одновременно (просто добавьте другой поток для обработки, как можете).

1 голос
/ 26 января 2012

Я думаю, что это хороший выбор. Тем не менее, фоновый работник несколько вытеснил .Net 4 framworks tasks . Это оптимизирует в зависимости от количества процессоров на вашей машине и соответственно оптимизирует работу. Возможно, вы могли бы использовать TPL и использовать параллель для . Вы можете передать максимальное количество одновременных потоков пула потоков для запуска, чтобы ограничить количество файлов, которые вы импортируете одновременно, в пакетах, например ::100100

ParallelOptions options = new ParallelOptions();  
options.MaxDegreeOfParallelism = 4;

Это может вам помочь?

1 голос
/ 26 января 2012

Может быть, да.Учтите, что существует только конечное количество процессоров или ядер.Только то много потоков может быть запущено одновременно.Вы могли бы иметь больше active , скажем, если многие из них будут ожидать другого процесса, запущенного на другом компьютере (например, если вы загружаете эти файлы).То, что у вас есть отдельный поток, не означает, что он добавляет параллелизм.Просто переключение затрат и выделение памяти (как вы видели).В зависимости от времени простоя, попробуйте ограничить ваш пул только несколькими потоками, чем процессор.Твик оттуда.

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

Если я вас правильно понял, вам нужно реализовать подход «производитель-потребитель»: 1) один производитель - создает список файлов (для импорта).2) несколько (фиксированное число) потребителей - выполните импорт.

Для достижения этого вы должны использовать BlockingCollection (начиная с .NET 4.0).В документации есть пример.

...