Posix threads

GitHub-classroom для самостоятельно изучающих курс не в рамках университетских программ

Ваша задача - реализовать классический паттерн producer-consumer с небольшими дополнительными условиями. Программа должна состоять из 3+N потоков:

Главный

producer

interruptor

N потоков consumer

В файл, чтение которого уже реализовано в шаблоне кода в блокирующкем режиме, пишется список чисел, разделённых пробелом (читать можно до переноса строки). Длина списка чисел не задаётся - считывание происходит до перевода каретки.

Задача producer-потока - получить на вход список чисел, и по очереди использовать каждое значение из этого списка для обновления переменной разделяемой между потоками

Задача consumer-потоков отреагировать на уведомление от producer и набирать сумму полученных значений. Также этот поток должен защититься от попыток потока-interruptor его остановить. Дополнительные условия:

Функция, исполняющая код этого потока consumer_routine, должна принимать указатель на объект/переменную, из которого будет читать обновления

После суммирования переменной поток должен заснуть на случайное количество миллисекунд, верхний предел будет передан на вход приложения (0 миллисекунд также должно корректно обрабатываться). Вовремя сна поток не должен мешать другим потокам consumer выполнять свои задачи, если они есть

Потоки consumer не должны дублировать вычисления друг с другом одних и тех же значений

В качестве возвращаемого значения поток должен вернуть свою частичную посчитанную сумму

Задача потока-interruptor проста: пока происходит процесс обновления значений, он должен постоянно пытаться остановить случайный поток consumer (вычисление случайного потока происходит перед каждой попыткой остановки). Как только поток producer произвел последнее обновление, этот поток завершается. В этом потоке можно выполнять вспомогательные действия которые помогут корректно обработать сигнал

Завершение приложения происходит или при считывании перевода каретки из файла или по посылке сигнала SIGTERM, обработку которого нужно также добавить. В обработчике сигнала можно вызывать только signal-safe функции https://man7.org/linux/man-pages/man7/signal-safety.7.html В случае, если в этот момент поток, читающий данные с файла, находится в режиме блокирующего чтения, - он также должен корректно завершиться. Полезно присмотреться к std::sig_atomic_t - позволяет потокобезопасно и signal-safe обращаться к переменной такого типа.

В нашем producer/consumer необходимо реализоваться потоковую обрботку данных для этих целей запрещается загружать весь файл в ОЗУ, иначе на больших файлах обработка не будет помещаться в ограничения ОЗУ на вычислительном устройстве.

Функция run_threads должна запускать все потоки, дожидаться их выполнения, и возвращать результат общего суммирования.

Для обеспечения межпоточного взаимодействия допускается использование только pthread API. На вход приложения передаётся 2 аргумента при старте именно в такой последовательности:

Число потоков consumer

Верхний предел сна consumer в миллисекундах

В поток вывода должно попадать только результирующее значение, по умолчанию никакой отладочной или запросной информации выводиться не должно. В случае детектирования ошибок нужно выдавать не нулевой код возврата.

#include <iostream> #include <fstream> #include <pthread.h> void* producer_routine(void* arg) { // Wait for consumer to start // Read data, loop through each value and update the value, notify consumer, wait for consumer to process std::ifstream ifs("in.txt"); // ... } void* consumer_routine(void* arg) { // notify about start // for every update issued by producer, read the value and add to sum // return pointer to result (for particular consumer) } void* consumer_interruptor_routine(void* arg) { // wait for consumers to start // interrupt random consumer while producer is running } int run_threads() { // start N threads and wait until they're done // return aggregated sum of values return 0; } int main() { std::cout << run_threads() << std::endl; return 0; }