mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-05 01:05:34 +08:00
refs #325, CWorker class for executing arbitrary tasks in their own threads
This commit is contained in:
committed by
Klaus Basan
parent
d4e126932b
commit
418dd5e126
46
src/blackmisc/worker.cpp
Normal file
46
src/blackmisc/worker.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/* Copyright (C) 2014
|
||||
* swift Project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||
* contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "worker.h"
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
|
||||
CWorker *CWorker::fromTask(QObject *owner, const QString &name, std::function<void()> task)
|
||||
{
|
||||
auto *thread = new CRegularThread(owner);
|
||||
auto *worker = new CWorker(task);
|
||||
|
||||
QString ownerName = owner->objectName().isEmpty() ? owner->metaObject()->className() : owner->objectName();
|
||||
thread->setObjectName(ownerName + ":" + name);
|
||||
worker->setObjectName(name);
|
||||
|
||||
worker->moveToThread(thread);
|
||||
QMetaObject::invokeMethod(worker, "ps_runTask");
|
||||
thread->start();
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
void CWorker::ps_runTask()
|
||||
{
|
||||
m_task();
|
||||
|
||||
QMutexLocker lock(&m_finishedMutex);
|
||||
m_finished = true;
|
||||
emit finished();
|
||||
lock.unlock();
|
||||
|
||||
auto *ownThread = thread();
|
||||
moveToThread(ownThread->thread()); // move worker back to the thread which constructed it, so there is no race on deletion
|
||||
QMetaObject::invokeMethod(ownThread, "deleteLater");
|
||||
QMetaObject::invokeMethod(this, "deleteLater");
|
||||
}
|
||||
|
||||
}
|
||||
143
src/blackmisc/worker.h
Normal file
143
src/blackmisc/worker.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/* Copyright (C) 2014
|
||||
* swift Project Community / Contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||
* contained in the LICENSE file.
|
||||
*/
|
||||
|
||||
//! \file
|
||||
|
||||
#ifndef BLACKMISC_WORKER_H
|
||||
#define BLACKMISC_WORKER_H
|
||||
|
||||
#include <QThread>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
|
||||
/*!
|
||||
* Just a subclass of QThread whose destructor waits for the thread to finish.
|
||||
*/
|
||||
class CRegularThread : public QThread
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CRegularThread(QObject *parent = nullptr) : QThread(parent) {}
|
||||
|
||||
//! Destructor
|
||||
~CRegularThread()
|
||||
{
|
||||
quit();
|
||||
wait();
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Class for doing some arbitrary task in its own thread.
|
||||
*
|
||||
* The task is exposed as a function object, so could be a lambda or a hand-written closure.
|
||||
* CWorker can not be subclassed, instead it can be extended with rich callable task objects.
|
||||
*/
|
||||
class CWorker final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/*!
|
||||
* Returns a new worker object which lives in a new thread.
|
||||
* \param owner Will be the parent of the new thread (the worker has no parent).
|
||||
* \param name A name for the task, which will be used to create a name for the thread.
|
||||
* \param task A function object which will be run by the worker in its thread.
|
||||
*/
|
||||
static CWorker *fromTask(QObject *owner, const QString &name, std::function<void()> task);
|
||||
|
||||
//! Connects to a slot which will be called when the task is finished.
|
||||
//! \threadsafe
|
||||
template <typename T, typename F>
|
||||
auto then(T *receiver, F slot) -> typename std::enable_if<std::is_member_function_pointer<F>::value>::type
|
||||
{
|
||||
Q_ASSERT(receiver->thread() == QThread::currentThread());
|
||||
QMutexLocker lock(&m_finishedMutex);
|
||||
connect(this, &CWorker::finished, receiver, slot);
|
||||
if (m_finished) { (receiver->*slot)(); }
|
||||
}
|
||||
|
||||
//! Connects to a functor which will be called when the task is finished.
|
||||
//! \threadsafe
|
||||
template <typename T, typename F>
|
||||
auto then(T *context, F functor) -> typename std::enable_if<! std::is_member_function_pointer<F>::value>::type
|
||||
{
|
||||
Q_ASSERT(context->thread() == QThread::currentThread());
|
||||
QMutexLocker lock(&m_finishedMutex);
|
||||
connect(this, &CWorker::finished, context, functor);
|
||||
if (m_finished) { functor(); }
|
||||
}
|
||||
|
||||
//! Connects to a functor which will be called when the task is finished.
|
||||
//! \threadsafe
|
||||
template <typename F>
|
||||
void then(F functor)
|
||||
{
|
||||
QMutexLocker lock(&m_finishedMutex);
|
||||
connect(this, &CWorker::finished, functor);
|
||||
if (m_finished) { functor(); }
|
||||
}
|
||||
|
||||
//! Returns true if the task has finished.
|
||||
//! \threadsafe But don't rely on this condition remaining true for any length of time.
|
||||
bool isFinished() const
|
||||
{
|
||||
QMutexLocker lock(&m_finishedMutex);
|
||||
return m_finished;
|
||||
}
|
||||
|
||||
//! Executes some code (in the caller's thread) if the task has finished.
|
||||
//! \threadsafe
|
||||
template <typename F>
|
||||
bool doIfFinished(F functor) const
|
||||
{
|
||||
QMutexLocker lock(&m_finishedMutex);
|
||||
if (m_finished) { functor(); }
|
||||
}
|
||||
|
||||
//! Executes some code (in the caller's thread) if the task has not finished.
|
||||
//! \threadsafe
|
||||
template <typename F>
|
||||
bool doIfNotFinished(F functor) const
|
||||
{
|
||||
QMutexLocker lock(&m_finishedMutex);
|
||||
if (! m_finished) { functor(); }
|
||||
}
|
||||
|
||||
//! Executes some code (in the caller's thread) if the task has not finished and some different code if it has finished.
|
||||
//! \threadsafe
|
||||
template <typename F1, typename F2>
|
||||
bool doIfFinishedElse(F1 ifFunctor, F2 elseFunctor) const
|
||||
{
|
||||
QMutexLocker lock(&m_finishedMutex);
|
||||
if (m_finished) { ifFunctor(); } else { elseFunctor(); }
|
||||
}
|
||||
|
||||
signals:
|
||||
//! Emitted when the task is finished.
|
||||
void finished();
|
||||
|
||||
private slots:
|
||||
//! Called when the worker has been moved into its new thread.
|
||||
void ps_runTask();
|
||||
|
||||
private:
|
||||
CWorker(std::function<void()> task) : m_task(task) {}
|
||||
|
||||
bool m_finished = false;
|
||||
mutable QMutex m_finishedMutex { QMutex::Recursive };
|
||||
std::function<void()> m_task;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user