线程通信的 myDesign 是否可以接受?
Is myDesign for thread communication acceptable?
我目前正在尝试为多个线程之间的通信创建一个良好且有效的 Qt
设计。我有一个偏好 window,它会在点击应用时发出不同的信号。例如,一个用于创建 SQL 连接,一个用于更改其他内容。我想在不同的 类 中更改后台线程中的首选项,并且在进行更改后它们将发出结果信号。在我的偏好 window 中,我现在想等到所有信号都收到(无论结果是真还是假),然后再关闭 window 或打印错误消息。
我试着在附图中画出我的设计。这是我的目的的正确方法吗?我目前正在为等待所有结果的方式而苦苦挣扎。我正在考虑创建某种数组来保存每个结果并检查数组,是否收到所有信号。但这听起来很丑陋......有没有更好的方法来等待所有信号都被接收到?
另外,将后台线程中的 类 作为 singelton
是个好主意吗?我只需要 类 的一个实例,这将使对 类 的访问变得非常容易,因为我不需要将指针拖到每个需要知道 类 的对象.
我还想知道,将 public 成员存储在 MySQL Class 中是否是个好主意,这告诉我数据库是否已连接并且直接从其他线程访问它?
谢谢!
QStateMachine
将完全按照您的要求进行操作:它可以在接收到信号时在状态之间转换。
后台线程可能不需要基于类,而且无论如何绝对不应该是单例。很可能你可以给 QtConcurrent::run
一个函子,并在那里发出信号。
逻辑应该分解成单独的 QObject
:
// https://github.com/KubaO/Whosebugn/tree/master/questions/thread-jobs-39109247
#include <QtWidgets>
#include <QtConcurrent>
#include <functional>
class Controller : public QObject {
Q_OBJECT
QStateMachine m_machine{this};
QState s_init{&m_machine};
QState s_busy{&m_machine};
QState s_idle{&m_machine};
int m_activeTasks = 0;
void onTaskStarted() {
++ m_activeTasks;
emit taskRunning();
}
void onTaskDone() {
if (--m_activeTasks == 0) emit allTasksDone();
}
Q_SIGNAL void taskRunning();
Q_SIGNAL void allTasksDone();
Q_SIGNAL void task1Done(int result);
Q_SIGNAL void task2Done(int result);
public:
Q_SIGNAL void active();
Q_SIGNAL void finished();
Q_SLOT void doTask1() {
onTaskStarted();
QtConcurrent::run([this]{
QThread::sleep(2); // pretend we do some work
emit task1Done(42);
});
}
Q_SLOT void doTask2() {
onTaskStarted();
QtConcurrent::run([this]{
QThread::sleep(5); // pretend we do some work
emit task2Done(44);
});
}
Controller(QObject * parent = nullptr) :
QObject{parent}
{
// This describes the state machine
s_init.addTransition(this, &Controller::taskRunning, &s_busy);
s_idle.addTransition(this, &Controller::taskRunning, &s_busy);
s_busy.addTransition(this, &Controller::allTasksDone, &s_idle);
m_machine.setInitialState(&s_init);
m_machine.start();
//
connect(this, &Controller::task1Done, this, [this](int result){
onTaskDone();
qDebug() << "task 1 is done with result" << result;
});
connect(this, &Controller::task2Done, this, [this](int result){
onTaskDone();
qDebug() << "task 2 is done with result" << result;
});
connect(&s_busy, &QState::entered, this, &Controller::active);
connect(&s_idle, &QState::entered, this, &Controller::finished);
}
};
Q_GLOBAL_STATIC(QStringListModel, model)
int main(int argc, char ** argv) {
using Q = QObject;
QApplication app{argc, argv};
Controller ctl;
QWidget w;
QFormLayout layout{&w};
QPushButton start1{"Start Task 1"};
QPushButton start2{"Start Task 2"};
QListView log;
layout.addRow(&start1);
layout.addRow(&start2);
layout.addRow(&log);
Q::connect(&start1, &QPushButton::clicked, &ctl, &Controller::doTask1);
Q::connect(&start2, &QPushButton::clicked, &ctl, &Controller::doTask2);
Q::connect(&ctl, &Controller::active, []{ qDebug() << "Active"; });
Q::connect(&ctl, &Controller::finished, []{ qDebug() << "Finished"; });
log.setModel(model);
qInstallMessageHandler(+[](QtMsgType, const QMessageLogContext &, const QString & msg){
auto row = model->rowCount();
model->insertRow(row);
model->setData(model->index(row), msg);
});
w.show();
return app.exec();
}
#include "main.moc"
我目前正在尝试为多个线程之间的通信创建一个良好且有效的 Qt
设计。我有一个偏好 window,它会在点击应用时发出不同的信号。例如,一个用于创建 SQL 连接,一个用于更改其他内容。我想在不同的 类 中更改后台线程中的首选项,并且在进行更改后它们将发出结果信号。在我的偏好 window 中,我现在想等到所有信号都收到(无论结果是真还是假),然后再关闭 window 或打印错误消息。
我试着在附图中画出我的设计。这是我的目的的正确方法吗?我目前正在为等待所有结果的方式而苦苦挣扎。我正在考虑创建某种数组来保存每个结果并检查数组,是否收到所有信号。但这听起来很丑陋......有没有更好的方法来等待所有信号都被接收到?
另外,将后台线程中的 类 作为 singelton
是个好主意吗?我只需要 类 的一个实例,这将使对 类 的访问变得非常容易,因为我不需要将指针拖到每个需要知道 类 的对象.
我还想知道,将 public 成员存储在 MySQL Class 中是否是个好主意,这告诉我数据库是否已连接并且直接从其他线程访问它?
谢谢!
QStateMachine
将完全按照您的要求进行操作:它可以在接收到信号时在状态之间转换。
后台线程可能不需要基于类,而且无论如何绝对不应该是单例。很可能你可以给 QtConcurrent::run
一个函子,并在那里发出信号。
逻辑应该分解成单独的 QObject
:
// https://github.com/KubaO/Whosebugn/tree/master/questions/thread-jobs-39109247
#include <QtWidgets>
#include <QtConcurrent>
#include <functional>
class Controller : public QObject {
Q_OBJECT
QStateMachine m_machine{this};
QState s_init{&m_machine};
QState s_busy{&m_machine};
QState s_idle{&m_machine};
int m_activeTasks = 0;
void onTaskStarted() {
++ m_activeTasks;
emit taskRunning();
}
void onTaskDone() {
if (--m_activeTasks == 0) emit allTasksDone();
}
Q_SIGNAL void taskRunning();
Q_SIGNAL void allTasksDone();
Q_SIGNAL void task1Done(int result);
Q_SIGNAL void task2Done(int result);
public:
Q_SIGNAL void active();
Q_SIGNAL void finished();
Q_SLOT void doTask1() {
onTaskStarted();
QtConcurrent::run([this]{
QThread::sleep(2); // pretend we do some work
emit task1Done(42);
});
}
Q_SLOT void doTask2() {
onTaskStarted();
QtConcurrent::run([this]{
QThread::sleep(5); // pretend we do some work
emit task2Done(44);
});
}
Controller(QObject * parent = nullptr) :
QObject{parent}
{
// This describes the state machine
s_init.addTransition(this, &Controller::taskRunning, &s_busy);
s_idle.addTransition(this, &Controller::taskRunning, &s_busy);
s_busy.addTransition(this, &Controller::allTasksDone, &s_idle);
m_machine.setInitialState(&s_init);
m_machine.start();
//
connect(this, &Controller::task1Done, this, [this](int result){
onTaskDone();
qDebug() << "task 1 is done with result" << result;
});
connect(this, &Controller::task2Done, this, [this](int result){
onTaskDone();
qDebug() << "task 2 is done with result" << result;
});
connect(&s_busy, &QState::entered, this, &Controller::active);
connect(&s_idle, &QState::entered, this, &Controller::finished);
}
};
Q_GLOBAL_STATIC(QStringListModel, model)
int main(int argc, char ** argv) {
using Q = QObject;
QApplication app{argc, argv};
Controller ctl;
QWidget w;
QFormLayout layout{&w};
QPushButton start1{"Start Task 1"};
QPushButton start2{"Start Task 2"};
QListView log;
layout.addRow(&start1);
layout.addRow(&start2);
layout.addRow(&log);
Q::connect(&start1, &QPushButton::clicked, &ctl, &Controller::doTask1);
Q::connect(&start2, &QPushButton::clicked, &ctl, &Controller::doTask2);
Q::connect(&ctl, &Controller::active, []{ qDebug() << "Active"; });
Q::connect(&ctl, &Controller::finished, []{ qDebug() << "Finished"; });
log.setModel(model);
qInstallMessageHandler(+[](QtMsgType, const QMessageLogContext &, const QString & msg){
auto row = model->rowCount();
model->insertRow(row);
model->setData(model->index(row), msg);
});
w.show();
return app.exec();
}
#include "main.moc"