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 или более потоков (лучше: задачи) для обработки очереди. Это позволит вам настроить количество потоков.