C# ThreadPool использует меньше потоков, чем ядра из-за ограничений памяти с FileSystemWatcher

У меня есть обширная задача вычисления изображения, которая использует около 1 ГБ памяти (один цикл расчета занимает около 4 секунд). Я обрабатываю эти изображения автоматически, когда они поступают в папку, используя FileSystemWatcher. Когда FileSystemWatcher запускает событие для нового файла, я помещаю работу в метод обработчика событий с помощью:

private void OnNewFileInDir(object source, FileSystemEventArgs evtArgs)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessTheNewImage), evtArgs.FullPath); 
}

Моя проблема в том, что программа происходит сбой на регулярной основе, когда файлы приходят быстро. В окне отладки я вижу, что в этот момент используется около 3 ГБ памяти. Когда я использую меньшие изображения, чтобы использовать меньше памяти, никаких сбоев (пока) нет.

Мой вопрос: что я могу сделать, чтобы использовать меньше (возможно, только 2) потоков независимо от ядер моего компьютера?

Или мой подход с использованием FileSystemWatcher для выдачи новых файлов в пул потоков совершенно глуп? У меня совсем нет опыта в гонках на нитках или подобных вещах. Итак, более того: это выглядит потокобезопасным?

Большое спасибо заранее и всего наилучшего

Тим

Для полноты здесь приведен код, выполняемый потоками (немного упрощенный для удобства чтения):

private void ProcessTheNewImage(object threadFilenameInfo)
{
   String filename = (String)threadFilenameInfo;

   // Load the image
   Image currentImage = Image.FromFile(filename);

   //Calculate the image in an external DLL
   Image currentResultImage = ImageProcessing.getResultImage(currentImage);

   //Create the filename with the result infos
   string saveFileName = "blahblah";

   //Save the image
   currentResultImage.Save(saveFileName);

   //dispose the images
   currentImage.Dispose();
   currentResultImage.Dispose();
}

1 ответ

Решение

Threadpool имеет очень ограниченную форму управления ресурсами. Он будет медленно добавлять потоки, когда очередь заполняется. Он предназначен для относительно небольших (< 500 мс) заданий. Там нет предохранительного клапана, чтобы остановить его засорение вашего приложения.

Для этого вы можете создать рабочий процесс: событие watcher помещает простые пакеты данных в ConcurrentQueue, а затем вы создаете 2 или более потоков (лучше: задачи) для обработки очереди. Это позволит вам настроить количество потоков.

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