Files
pip/libs/main/thread/pithreadpoolloop.cpp
peri4 e57719c118 version 4.7.2
PIThreadPoolLoop reworked, now threads starts on constructor and immediately run after start()
2025-06-07 10:58:11 +03:00

169 lines
5.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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 <http://www.gnu.org/licenses/>.
*/
#include "pithreadpoolloop.h"
#include "piliterals_time.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<int> 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<int> 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([this]() {
while (true) {
sem_exec.acquire();
if (is_destroy) return;
int cc = counter.fetch_add(1);
func(cc);
sem_done.release();
}
});
threads << t;
}
for (auto * t: threads)
t->start();
// piCout << "PIThreadPoolLoop" << proc_cnt << "threads";
}
PIThreadPoolLoop::~PIThreadPoolLoop() {
is_destroy = true;
for (auto * t: threads)
t->stop();
sem_exec.release(threads.size());
for (auto * t: threads) {
if (!t->waitForFinish(100_ms)) t->terminate();
delete t;
}
}
void PIThreadPoolLoop::setFunction(std::function<void(int)> f) {
func = f;
}
void PIThreadPoolLoop::wait() {
// piCout << "wait" << wait_count;
if (wait_count <= 0) return;
sem_done.acquire(wait_count);
wait_count = 0;
// piCout << "wait done";
}
void PIThreadPoolLoop::start(int index_start, int index_count) {
counter = index_start;
wait_count = index_count;
sem_exec.release(index_count);
}
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<void(int)> f) {
setFunction(f);
exec(index_start, index_count);
}