From dedc35b46690e94b4c03e2e4d98bf9d6441cb62c Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 24 Sep 2021 16:03:20 +0300 Subject: [PATCH] new class PIThreadPoolLoop --- CMakeLists.txt | 2 +- libs/main/io_devices/pifile.h | 1 + libs/main/thread/pithreadmodule.h | 3 +- libs/main/thread/pithreadpoolloop.cpp | 130 ++++++++++++++++++++++++++ libs/main/thread/pithreadpoolloop.h | 69 ++++++++++++++ main.cpp | 14 +++ 6 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 libs/main/thread/pithreadpoolloop.cpp create mode 100644 libs/main/thread/pithreadpoolloop.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ad927ed6..df3aa574 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(pip) set(pip_MAJOR 2) set(pip_MINOR 31) -set(pip_REVISION 0) +set(pip_REVISION 1) set(pip_SUFFIX ) set(pip_COMPANY SHS) set(pip_DOMAIN org.SHS) diff --git a/libs/main/io_devices/pifile.h b/libs/main/io_devices/pifile.h index 9270ae09..2cb9c036 100644 --- a/libs/main/io_devices/pifile.h +++ b/libs/main/io_devices/pifile.h @@ -85,6 +85,7 @@ public: }; //! Constructs a file with path "path" and open mode "mode" + //! If "path" is not empty then open file explicit PIFile(const PIString & path, DeviceMode mode = ReadWrite); diff --git a/libs/main/thread/pithreadmodule.h b/libs/main/thread/pithreadmodule.h index cb1128e1..18706e05 100644 --- a/libs/main/thread/pithreadmodule.h +++ b/libs/main/thread/pithreadmodule.h @@ -26,7 +26,8 @@ #include "pitimer.h" #include "pipipelinethread.h" #include "pigrabberbase.h" -#include "pithreadpoolexecutor.h" #include "piconditionvar.h" +#include "pithreadpoolexecutor.h" +#include "pithreadpoolloop.h" #endif // PITHREADMODULE_H diff --git a/libs/main/thread/pithreadpoolloop.cpp b/libs/main/thread/pithreadpoolloop.cpp new file mode 100644 index 00000000..b829469b --- /dev/null +++ b/libs/main/thread/pithreadpoolloop.cpp @@ -0,0 +1,130 @@ +/* + 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" + + +/*! \class PIThreadPoolLoop + * @brief Thread class + * \details This class allow you parallelize loop. + * + * \section PIThreadPoolLoop_sec0 Usage + * This class designed to replace "for(;;)" statement in very simple way. + * In constructor several threads created, then by "setFunction()" method + * you should pass body of your loop, and then call "start()" or "exec()". + * Every thread take loop counter and execute your function until all + * counter range is passed. + * + * Example: +\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 + * + * Equivalent to: +\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 + * + * \section PIThreadPoolLoop_sec1 Important + * 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. + * + */ + + +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(); +} diff --git a/libs/main/thread/pithreadpoolloop.h b/libs/main/thread/pithreadpoolloop.h new file mode 100644 index 00000000..30938d66 --- /dev/null +++ b/libs/main/thread/pithreadpoolloop.h @@ -0,0 +1,69 @@ +/*! @file pithreadpoolloop.h + * @brief Thread pool loop + * + * This file declare thread class and some wait functions +*/ +/* + 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 . +*/ + +#ifndef PITHREADPOOLLOOP_H +#define PITHREADPOOLLOOP_H + +#include "pivector.h" + +class PIThread; + +class PIP_EXPORT PIThreadPoolLoop { +public: + + //! Contructs thread pool with threads count "thread_cnt". + //! If "thread_cnt" = -1 then system processors count used + PIThreadPoolLoop(int thread_cnt = -1); + + virtual ~PIThreadPoolLoop(); + + //! Set threads function to "f" with format [](int){...} + void setFunction(std::function f); + + //! Wait for all threads stop + void wait(); + + //! Start functions execution with integer argument range + //! from "index_start" to "index_start + index_count - 1" + void start(int index_start, int index_count); + + //! Start functions execution with integer argument range + //! from "index_start" to "index_start + index_count - 1" + //! and wait for finish + void exec(int index_start, int index_count); + + //! Start functions "f" execution with integer argument range + //! from "index_start" to "index_start + index_count - 1" + //! and wait for finish + void exec(int index_start, int index_count, std::function f); + +private: + PIVector threads; + std::function func; + std::atomic_int counter; + +}; + + +#endif diff --git a/main.cpp b/main.cpp index 1ed2e829..1779d4dd 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,20 @@ int main(int argc, char * argv[]) { + PIVector data(10, [](int i)->int{return i;}); + + piCout << data; + + PIThreadPoolLoop pool; + pool.setFunction([&](int i){ + data[i] = data[i] + 10; + }); + pool.exec(0, data.size()); + + piCout << data; + + return 0; + PIVector x(20, [](int i) {return i;}); piCout << x; piCout << x.any([](int v) {return v == 10;});