Мы разрабатываем приложение, в котором пользователи могут запускать несколько задач одновременно. Мы используем ThreadPool.QueueUserWorkItem для запуска задач. Эта часть работает хорошо.
У нас есть проблема, когда эти задачи могут потреблять более 500 МБ памяти. Мы используем ввод-вывод с отображением памяти для управления памятью. Тем не менее, когда пользователи устанавливают более 10 задач, работающих одновременно, пул потоков запускает их все, и были случаи, когда у нас заканчивалась память и возникали исключения. Мы прекрасно справляемся с ошибками.
Мне интересно, есть ли способ учесть память, которая будет потребляться при обработке очереди, то есть держать задачи в очереди до тех пор, пока не будет достаточно памяти? Могу ли я сообщить пулу потоков о том, сколько памяти мы будем запрашивать (что мы можем приблизительно оценить)?
Ответ 1 · usr
ThreadPool ничего не знает о том, что вы делаете в своих задачах. Вы должны убедиться в этом сами. Вы можете управлять глобальной переменной типа long, представляющей общее количество байтов, которое, вероятно, потребуется всем работающим заданиям на пике нагрузки. Когда пул потоков планирует одну из ваших задач, вы сначала проверяете эту переменную. Если он уже слишком высок, подождите, пока не завершится любая работающая в данный момент задача. Затем вы снова проверяете.
Низкотехнологичное решение этой проблемы - использовать опрос с интервалом ожидания 100 мс. В высокотехнологичной версии использовалась бы какая-то схема ожидания, включающая события.
Ответ 2 · Martin James
Хорошо, если вы можете получить оценку памяти для каждой задачи, вы, вероятно, могли бы сделать это, сохраняя в пуле приблизительный подсчет использования памяти, защищенный CS. Добавьте к этому счету непосредственно перед отправкой задачи, и пусть задача вызовет функцию memRelease, чтобы вычесть из нее непосредственно перед ее завершением и проверить, можно ли что-либо запустить сейчас (см. Ниже).
Если какой-то поток хочет отправить задачу и обнаруживается (путем сравнения его потребностей с текущим использованием внутри CS), что в "бюджете" недостаточно памяти для его запуска, вы можете засунуть его в параллельную очередь. /list ждать, пока не будет. Всякий раз, когда задача завершается и вызывает "memRelease", она добавляется к багету памяти и перебирает очередь/список (сначала блокируя его), чтобы попытаться найти что-то, что теперь можно запустить с увеличенной доступной памятью. Если это так, он отправляет задачу в пул.
Ответ 3 · Vlad Bezden
Вы можете контролировать количество потоков в пуле потоков с помощью ThreadPool.SetMaxThreads. Итак, что вы можете сделать, это установить максимальное количество потоков
ThreadPool.SetMaxThreads = new PerformanceCounter("Memory", "Available MBytes").RawValue / 500;
PerformanceCounter("Память", "Доступные МБ").RawValue -> Возвращает доступную память в МБ