【问题标题】:Is myDesign for thread communication acceptable?用于线程通信的 myDesign 是否可以接受?
【发布时间】:2016-08-23 19:27:42
【问题描述】:

我目前正在尝试为多个线程之间的通信创建一个良好且有效的Qt 设计。我有一个首选项窗口,它会在单击应用时发出不同的信号。例如,一个用于创建 SQL 连接,另一个用于更改其他内容。我想在不同类的后台线程中更改首选项,并在进行更改后发出结果信号。在我的首选项窗口中,我现在想等到收到所有信号(结果为真或假),然后再关闭窗口或打印错误消息。

我试着在附图中画出我的设计。这是我的目的的正确方法吗?我目前正在努力等待所有结果。我正在考虑创建某种数组来保存每个结果并检查数组是否接收到所有信号。但这听起来很丑陋……有没有更好的方法来等到所有信号都接收到?

将后台线程中的类设为singelton 也是一个好主意吗?我只需要一个类的实例,这样就可以很容易地访问这些类,因为我不需要将指针拖到每个需要知道类的对象上。

我也想知道,在 MySQL 类中存储一个公共成员是否是一个好主意,它告诉我数据库是否已连接并直接从其他线程访问它?

谢谢!

【问题讨论】:

    标签: c++ multithreading qt communication signals-slots


    【解决方案1】:

    QStateMachine 将完全按照您的意愿行事:它可以在收到信号时在状态之间转换。

    后台线程可能不需要基于类,无论如何它们绝对不应该是单例。您很可能可以给QtConcurrent::run 一个函子,并在那里发出信号。

    逻辑应该被分解成一个单独的QObject

    // https://github.com/KubaO/stackoverflown/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"
    

    【讨论】:

    • 感谢您的回答!我现在不知道QtConcurrent::run 是否适合我,我现在不能说,但我想我理解这个概念。在这种情况下,单例有什么问题?否则我将需要每个需要与该线程通信以知道指向该对象的指针的类,这听起来很复杂......
    • QStateMachine这个概念我不是很懂,可以举个小例子吗?据我了解,我必须为每个可能的信号和每个类的所属结果代码创建一个状态。我会用一个状态初始化它们,告诉我它们尚未处理。在我的等待过程中,我会不断地检查每个状态,直到每个状态都有一个有效状态。我理解正确吗?
    • 首先,您假设有 线程。你可能不需要关心它。让QThreadPool 以最有效的方式管理线程,并使用QtConcurrent::run 向它们提交工作。控制器 - 业务逻辑 - 可以是单个 QObject,它提交要同时完成的工作并对结果采取行动,请参阅编辑。
    • 状态机概念在任何软件开发中都非常重要,您应该熟悉它。您可能需要也可能不需要为每个信号创建一个状态,这实际上取决于您想要做什么。
    • “在我的等待过程中,我会不断地检查每个状态,直到每个状态都有一个有效的状态。”忘记等待任何事情。在现代异步代码中,没有等待。曾经。事件循环等待。当事情发生时您会得到通知 - 您的代码会根据发生的事情运行。
    猜你喜欢
    • 1970-01-01
    • 2011-03-31
    • 1970-01-01
    • 2016-01-19
    • 2012-06-16
    • 2011-07-27
    • 1970-01-01
    • 2020-04-08
    • 1970-01-01
    相关资源
    最近更新 更多