work with PIThreadPoolWorker
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
|
#ifndef PITHREADPOOLEXECUTOR_H
|
||||||
|
#define PITHREADPOOLEXECUTOR_H
|
||||||
|
|
||||||
#include "pithreadpoolworker.h"
|
#include "pithreadpoolworker.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// [[deprecated("DeprecatedHeader.h is deprecated. Use NewHeader.h instead.")]]
|
|
||||||
DEPRECATEDM("Use pithreadpoolworker.h") const bool deprecated_header_is_used = true;
|
DEPRECATEDM("Use pithreadpoolworker.h") const bool deprecated_header_is_used = true;
|
||||||
} // namespace
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -42,23 +42,19 @@ PIThreadPoolWorker::PIThreadPoolWorker(int threads_count) {
|
|||||||
|
|
||||||
|
|
||||||
PIThreadPoolWorker::~PIThreadPoolWorker() {
|
PIThreadPoolWorker::~PIThreadPoolWorker() {
|
||||||
stop();
|
|
||||||
for (auto * w: workers) {
|
|
||||||
if (!w->thread.waitForFinish(1_s)) w->thread.terminate();
|
|
||||||
}
|
|
||||||
piDeleteAllAndClear(workers);
|
piDeleteAllAndClear(workers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIThreadPoolWorker::start() {
|
void PIThreadPoolWorker::start() {
|
||||||
for (auto w: workers)
|
for (auto w: workers) {
|
||||||
w->thread.start();
|
w->thread.start();
|
||||||
m_running = true;
|
w->notifier.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIThreadPoolWorker::stop() {
|
void PIThreadPoolWorker::stop() {
|
||||||
m_running = false;
|
|
||||||
for (auto w: workers) {
|
for (auto w: workers) {
|
||||||
w->thread.stop();
|
w->thread.stop();
|
||||||
w->notifier.notify();
|
w->notifier.notify();
|
||||||
@@ -102,13 +98,87 @@ bool PIThreadPoolWorker::waitForFinish(PISystemTime timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::isRunning() const {
|
||||||
|
return workers.every([](Worker * w) { return w->thread.isRunning(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::waitForTasks(PISystemTime timeout) {
|
||||||
|
if (!isRunning()) return tasks_queue.getRef()->isEmpty();
|
||||||
|
auto checkWorking = [this] {
|
||||||
|
if (tasks_queue.getRef()->isNotEmpty()) return true;
|
||||||
|
for (auto * w: workers)
|
||||||
|
if (w->in_work > 0) return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (timeout.isNull()) {
|
||||||
|
for (;;) {
|
||||||
|
if (!checkWorking()) break;
|
||||||
|
piMinSleep();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PITimeMeasurer tm;
|
||||||
|
while (tm.elapsed() < timeout) {
|
||||||
|
if (!checkWorking()) return true;
|
||||||
|
piMinSleep();
|
||||||
|
}
|
||||||
|
return tm.elapsed() < timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIThreadPoolWorker::waitForTask(int64_t id, PISystemTime timeout) {
|
||||||
|
if (!isRunning()) return tasks_queue.getRef()->every([id](const Task & t) { return t.id != id; });
|
||||||
|
auto checkWorking = [this, id] {
|
||||||
|
if (tasks_queue.getRef()->any([id](const Task & t) { return t.id == id; })) return true;
|
||||||
|
for (auto * w: workers)
|
||||||
|
if (w->in_work == id) return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (timeout.isNull()) {
|
||||||
|
for (;;) {
|
||||||
|
if (!checkWorking()) break;
|
||||||
|
piMinSleep();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PITimeMeasurer tm;
|
||||||
|
while (tm.elapsed() < timeout) {
|
||||||
|
if (!checkWorking()) return true;
|
||||||
|
piMinSleep();
|
||||||
|
}
|
||||||
|
return tm.elapsed() < timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIThreadPoolWorker::exec() {
|
||||||
|
start();
|
||||||
|
waitForStart();
|
||||||
|
waitForTasks();
|
||||||
|
stopAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int64_t PIThreadPoolWorker::enqueueTask(std::function<void(int64_t)> func, PIObject * context) {
|
int64_t PIThreadPoolWorker::enqueueTask(std::function<void(int64_t)> func, PIObject * context) {
|
||||||
|
if (context) {
|
||||||
|
if (!contexts[context]) {
|
||||||
|
contexts << context;
|
||||||
|
CONNECTL(context, deleted, ([this, context](PIObject *) {
|
||||||
|
contexts.remove(context);
|
||||||
|
auto qref = tasks_queue.getRef();
|
||||||
|
// auto prev_size = qref->size();
|
||||||
|
// piCout << "deleted" << (void *)context << qref->map<void *>([](const Task & t) { return t.context; });
|
||||||
|
qref->removeWhere([context](const Task & t) { return t.context == context; });
|
||||||
|
// piCout << prev_size << qref->size() << qref->map<void *>([](const Task & t) { return t.context; });
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
int64_t id = ++next_task_id;
|
int64_t id = ++next_task_id;
|
||||||
Task task;
|
Task task;
|
||||||
task.id = id;
|
task.id = id;
|
||||||
task.func = std::move(func);
|
task.func = std::move(func);
|
||||||
task.context = context;
|
task.context = context;
|
||||||
tasks_queue.getRef()->append(std::move(task));
|
tasks_queue.getRef()->enqueue(std::move(task));
|
||||||
for (auto * w: workers)
|
for (auto * w: workers)
|
||||||
w->notifier.notify();
|
w->notifier.notify();
|
||||||
return id;
|
return id;
|
||||||
@@ -142,7 +212,6 @@ PIThreadPoolWorker::TaskStatus PIThreadPoolWorker::taskStatus(int64_t id) const
|
|||||||
void PIThreadPoolWorker::threadFunc(Worker * w) {
|
void PIThreadPoolWorker::threadFunc(Worker * w) {
|
||||||
w->notifier.wait();
|
w->notifier.wait();
|
||||||
if (w->thread.isStopping()) return;
|
if (w->thread.isStopping()) return;
|
||||||
if (!m_running) return;
|
|
||||||
Task task;
|
Task task;
|
||||||
{
|
{
|
||||||
auto ref = tasks_queue.getRef();
|
auto ref = tasks_queue.getRef();
|
||||||
@@ -151,7 +220,9 @@ void PIThreadPoolWorker::threadFunc(Worker * w) {
|
|||||||
}
|
}
|
||||||
if (!task.isValid()) return;
|
if (!task.isValid()) return;
|
||||||
w->in_work = task.id;
|
w->in_work = task.id;
|
||||||
|
taskStarted(task.id);
|
||||||
task.func(task.id);
|
task.func(task.id);
|
||||||
w->in_work = -1;
|
w->in_work = -1;
|
||||||
|
taskFinished(task.id);
|
||||||
w->notifier.notify();
|
w->notifier.notify();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,9 @@
|
|||||||
//! \~\brief
|
//! \~\brief
|
||||||
//! \~english Fixed-size pool of worker threads for fire-and-forget tasks.
|
//! \~english Fixed-size pool of worker threads for fire-and-forget tasks.
|
||||||
//! \~russian Фиксированный пул рабочих потоков для задач без ожидания результата.
|
//! \~russian Фиксированный пул рабочих потоков для задач без ожидания результата.
|
||||||
class PIP_EXPORT PIThreadPoolWorker {
|
class PIP_EXPORT PIThreadPoolWorker: public PIObject {
|
||||||
|
PIOBJECT(PIThreadPoolWorker)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//! \~english Constructs executor with \a threads_count worker threads.
|
//! \~english Constructs executor with \a threads_count worker threads.
|
||||||
//! \~russian Создает исполнитель с \a threads_count рабочими потоками.
|
//! \~russian Создает исполнитель с \a threads_count рабочими потоками.
|
||||||
@@ -78,8 +80,17 @@ public:
|
|||||||
|
|
||||||
//! \~english Returns whether the threads are currently running.
|
//! \~english Returns whether the threads are currently running.
|
||||||
//! \~russian Возвращает, выполняются ли потоки в данный момент.
|
//! \~russian Возвращает, выполняются ли потоки в данный момент.
|
||||||
bool isRunning() const { return m_running; }
|
bool isRunning() const;
|
||||||
|
|
||||||
|
//! \~english Waits for all tasks completion. Returns \b false if the timeout expires first.
|
||||||
|
//! \~russian Ожидает завершения всех задач. Возвращает \b false, если таймаут истек раньше.
|
||||||
|
bool waitForTasks(PISystemTime timeout = {});
|
||||||
|
|
||||||
|
//! \~english Waits for task with id \a id completion. Returns \b false if the timeout expires first.
|
||||||
|
//! \~russian Ожидает завершения задачи с id \a id. Возвращает \b false, если таймаут истек раньше.
|
||||||
|
bool waitForTask(int64_t id, PISystemTime timeout = {});
|
||||||
|
|
||||||
|
void exec();
|
||||||
|
|
||||||
int64_t enqueueTask(std::function<void(int64_t)> func, PIObject * context = nullptr);
|
int64_t enqueueTask(std::function<void(int64_t)> func, PIObject * context = nullptr);
|
||||||
|
|
||||||
@@ -89,18 +100,38 @@ public:
|
|||||||
|
|
||||||
template<typename O>
|
template<typename O>
|
||||||
int64_t enqueueTask(O * obj, void (O::*member_func)(int64_t)) {
|
int64_t enqueueTask(O * obj, void (O::*member_func)(int64_t)) {
|
||||||
return enqueueTask([obj, member_func](int64_t id) { (obj->*member_func)(id); });
|
return enqueueTask([obj, member_func](int64_t id) { (obj->*member_func)(id); },
|
||||||
|
PIObject::isPIObject(obj) ? dynamic_cast<PIObject *>(obj) : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename O>
|
template<typename O>
|
||||||
int64_t enqueueTask(O * obj, void (O::*member_func)()) {
|
int64_t enqueueTask(O * obj, void (O::*member_func)()) {
|
||||||
return enqueueTask([obj, member_func](int64_t) { (obj->*member_func)(); });
|
return enqueueTask([obj, member_func](int64_t) { (obj->*member_func)(); },
|
||||||
|
PIObject::isPIObject(obj) ? dynamic_cast<PIObject *>(obj) : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool removeTask(int64_t id);
|
bool removeTask(int64_t id);
|
||||||
|
void clearTasks();
|
||||||
|
|
||||||
TaskStatus taskStatus(int64_t id) const;
|
TaskStatus taskStatus(int64_t id) const;
|
||||||
|
|
||||||
|
//! \events
|
||||||
|
//! \{
|
||||||
|
|
||||||
|
//! \fn void taskStarted(int64_t id)
|
||||||
|
//! \brief
|
||||||
|
//! \~english Raised on start execution task with id \a id.
|
||||||
|
//! \~russian Вызывается при старте выполнения задачи с id \a id.
|
||||||
|
EVENT1(taskStarted, int64_t, id);
|
||||||
|
|
||||||
|
//! \fn void taskFinished(int64_t id)
|
||||||
|
//! \brief
|
||||||
|
//! \~english Raised on finish execution task with id \a id.
|
||||||
|
//! \~russian Вызывается при завершении выполнения задачи с id \a id.
|
||||||
|
EVENT1(taskFinished, int64_t, id);
|
||||||
|
|
||||||
|
//! \}
|
||||||
|
|
||||||
|
|
||||||
// DEPRECATED
|
// DEPRECATED
|
||||||
|
|
||||||
@@ -114,7 +145,7 @@ public:
|
|||||||
//! \~russian
|
//! \~russian
|
||||||
//! Это вызов по принципу best-effort без ожидания результата и без сообщения о том, была ли задача принята.
|
//! Это вызов по принципу best-effort без ожидания результата и без сообщения о том, была ли задача принята.
|
||||||
//! \После запроса на завершение новые задачи игнорируются.
|
//! \После запроса на завершение новые задачи игнорируются.
|
||||||
void execute(std::function<void()> runnable);
|
void execute(std::function<void()> runnable) { enqueueTask(runnable); }
|
||||||
|
|
||||||
void shutdownNow() DEPRECATEDM("Use stopAndWait()") { stopAndWait(); }
|
void shutdownNow() DEPRECATEDM("Use stopAndWait()") { stopAndWait(); }
|
||||||
void shutdown() DEPRECATEDM("Use stop()") { stop(); }
|
void shutdown() DEPRECATEDM("Use stop()") { stop(); }
|
||||||
@@ -139,7 +170,7 @@ private:
|
|||||||
mutable PIVector<Worker *> workers;
|
mutable PIVector<Worker *> workers;
|
||||||
mutable PIProtectedVariable<PIQueue<Task>> tasks_queue;
|
mutable PIProtectedVariable<PIQueue<Task>> tasks_queue;
|
||||||
|
|
||||||
std::atomic_bool m_running = {false};
|
PISet<PIObject *> contexts;
|
||||||
std::atomic_int64_t next_task_id = {0};
|
std::atomic_int64_t next_task_id = {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user