/* PIP - Platform Independent Primitives Thread pool loop Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "pithreadpoolloop.h" #include "pisysteminfo.h" #include "pithread.h" //! \addtogroup Thread //! \{ //! \class PIThreadPoolLoop pithreadpoolloop.h //! \~\brief //! \~english Thread pool loop //! \~russian Пул потоков //! //! \~\details //! \~english //! This class allow you parallelize loop. //! //! \~russian //! Этот класс позволяет распараллелить цикл //! //! //! \~english \section PIThreadPoolLoop_sec0 Usage //! \~russian \section PIThreadPoolLoop_sec0 Использование //! \~english //! This class designed to parallel "for(;;)" statement in very simple way. //! In constructor several threads created, then by \a setFunction() method //! you should pass body of your loop, and then call \a start() or \a exec(). //! Every thread take loop counter and execute your function until all //! counter range is passed. //! //! Example: //! \~russian //! Этот класс предназначен для распараллеливания цикла "for(;;)" максимально простым способом. //! В конструкторе создается несколько потоков, затем методом \a setFunction() //! устанавливается функция, представляющая собой тело цикла. Затем вызовом //! \a start() или \a exec() цикл исполняется параллельно. Каждый поток получает //! значение переменной цикла и вызывает функцию-тело до тех пор, пока //! весь диапазон не будет исполнен. //! //! Пример: //! //! \~\code{.cpp} //! PIVector data(10, [](int i)->int{return i;}); //! //! piCout << data; // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} //! //! PIThreadPoolLoop pool; //! pool.exec(0, data.size(), [&](int i){ // parallel analogue "for (int i = 0; i < data.size(); i++)" //! data[i] = data[i] + 10; //! }); //! //! piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19} //! \endcode //! //! \~english Equivalent to: //! \~russian Эквивалентно: //! \~\code{.cpp} //! PIVector data(10, [](int i)->int{return i;}); //! //! piCout << data; // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} //! //! pool.setFunction([&](int i){ //! data[i] = data[i] + 10; //! }); //! pool.exec(0, data.size()); //! //! piCout << data; // {10, 11, 12, 13, 14, 15, 16, 17, 18, 19} //! \endcode //! //! //! \~english \section PIThreadPoolLoop_sec1 Important //! \~russian \section PIThreadPoolLoop_sec1 Важно //! \~english //! Due to multithreading it`s very important to protect output data of loop body, use mutex. //! Also remember that execution order is undefined and you shouldn`t use global variables in //! your function. Use local variables and lambda capture. //! //! \~russian //! В силу многопоточности очень важно защитить выходные данные тела цикла с помощью блокировок (мьютекса). //! Также стоит помнить, что последовательность выполнения неопределена, и не стоит использовать глобальных //! переменных в теле цикла. Используйте локальные переменные и захват в лямбде. //! //! \} PIThreadPoolLoop::PIThreadPoolLoop(int thread_cnt) { if (thread_cnt <= 0) thread_cnt = piMaxi(1, PISystemInfo::instance()->processorsCount); piForTimes (thread_cnt) { auto * t = new PIThread(); threads << t; } //piCout << "PIThreadPoolLoop" << proc_cnt << "threads"; } PIThreadPoolLoop::~PIThreadPoolLoop() { for (auto * t: threads) { t->stop(false); if (!t->waitForFinish(100)) t->terminate(); delete t; } } void PIThreadPoolLoop::setFunction(std::function f) { func = f; } void PIThreadPoolLoop::start(int index_start, int index_count) { counter = index_start; int end = index_start + index_count; for (auto * t: threads) t->start([this,end,t](){ while (1) { int cc = counter.fetch_add(1); if (cc >= end) { t->stop(false); return; } func(cc); } }); } void PIThreadPoolLoop::exec(int index_start, int index_count) { start(index_start, index_count); wait(); } void PIThreadPoolLoop::exec(int index_start, int index_count, std::function f) { setFunction(f); exec(index_start, index_count); } void PIThreadPoolLoop::wait() { for (auto * t: threads) t->waitForFinish(); }