Мы предлагаем реализовать 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. Данная функция доступна только в реализациях с типом возвращаемого значения отличным отvoidset_priority- задать приоритетAction
Для передачи функции на выполнение в поток используется класс Thread, который определяет непосредственно поток - std::thread и loop функцию для запуска Action в потоке. loop в бесконечном цикле ожидает появления нового Action. После добавления Action в Thread сохраненная функция запускается на выполнение. Для уведомления ThreadPool об завершении функции предусмотрен callback. Как только один из потоков освободился ThreadPool выбирает новый Action из сохраненных, с учетом заданного приоритета.