Мы предлагаем реализовать ThreadPool, который может исполнять любую работу в одном из заранее созданных рабочих потоков. И консольное приложение, которое демонстрирует работу ThreadPool. Если пользователь ввёл restart, то существующий ThreadPool уничтожается и создаётся новый с большим количеством рабочих потоков. Если пользователь ввёл exit, то приложение завершается. Если пользователь ввёл два числа, то приложение отправляет работу в ThreadPool по вычислению всех простых делителей первого числа (uint64), а второе число нужно для задания приоритета этой задаче. После завершения работы нужно вывести результат в консоль.
Мы хотим получить программу на современном C++, ведь в современном C++ есть потоки, примитивы синхронизации и много всего хорошего. Для сборки лучше всего использовать CMake. Ещё мы хотим получить текст, который описывает детали реализации.
Уверены, вы блестяще справитесь с заданием!
Писалось все и тестилось на линуксе, но сам код, вроде как получился кросплатформенный.
Перейти в каталог с проектом, создать папку build
, сконфигурировать cmake
.
cd test_thread_pool
mkdir build
cd build
cmake ..
После запустить сборку проекта.
make
Для теста запустить исполняемый файл thrdpool_task
, на экране отобразится приглашение к вводу.
> cd
Доступны следующие команды:
help
- показать список командrestart
- перезапустить пул потоков, задачи в пуле будут уничтожены, при перезапуске, будет добавлен на один поток больше.exit
- завершение работы. Перед завершение программа дождется выполнения всех задач в очереди.<N> <P>
- число и приоритет. Будет добавлена задача по поиску списка простых делителей числа N. Если есть свободные потоки, то задача будет запущена немедленно, иначе задача будет сохранена в списке на ожидание с приоритетом P. Задачи с наибольшим приоритетом покидают список ожидания первыми.ctrl+d
- аналогичноexit
Для теста можно запустить несколько задач на выполнение. Удобно просто скопировать и вставить в окно терминала.
239847298479287487 1
239847298449822834 2
23984733448729834 3
239847298475472934 4
213123 5
98987609808098111 6
230875890000000009 7
2398488888834 8
4729847545871123 9
213123 10
В процессе завершения задач на экране будет отображаться результат выполнения.
При создании объекта ThreadPool
создается набор потоков Thread
. Если в качестве аргумента в конструктор ThreadPool
передать число num
, то будет создано num
потоков, если num == 0
, то количество потоков определяется функцией std::thread::hardware_concurrency()
.
Для хранения действий используется класс Action
, для которого добавлены шаблонные реализации ActionImpl
, имеющие разную реализацию в зависимости от типа возвращаемого значения у переданной функции. ActionImpl<R>
воспринимает только функции R func()
(func возвращает результат типа R и не принимает никаких аргументов).
Для добавления новых действий пользователем в классе ThreadPool
есть шаблонные функции run_action
, имеющие перегрузки для трех разных типов вызываемых объектов:
- функция член класса
- обычная функция
- другой вызываемый объект, соответствующий
std::is_invokable
Для приведения пользовательских функций к R func()
используется std::bind
.
run_action
создает новый Action
и возвращает пользователю объект ActionResult
. Созданный Action
запускается одним из свободных потоков, а если нет ни одного свободного потока, то сохраняется в списке для ожидания. ActionResult
предоставляет пользователю функции для управления Action
:
wait
- блокирует текущий поток до завершенияAction
, еслиAction
уже был выполнен, то управление в поток возвращается немедленноres
- возвращает результат выполнения функции вAction
. Данная функция доступна только в реализациях с типом возвращаемого значения отличным отvoid
set_priority
- задать приоритетAction
Для передачи функции на выполнение в поток используется класс Thread
, который определяет непосредственно поток - std::thread
и loop
функцию для запуска Action
в потоке. loop
в бесконечном цикле ожидает появления нового Action
. После добавления Action
в Thread
сохраненная функция запускается на выполнение. Для уведомления ThreadPool
об завершении функции предусмотрен callback. Как только один из потоков освободился ThreadPool
выбирает новый Action
из сохраненных, с учетом заданного приоритета.