В асинхронном программировании «ожидание» считается анти-шаблоном. Вместо того, чтобы ждать вещей, создайте код, чтобы реагировать на условие, которое будет выполнено. Например, подключите код к сигналу.
Один из способов реализации этого состоит в том, чтобы разрезать ваши действия на отдельные состояния и выполнять некоторую работу, когда вводится каждое из состояний. Конечно, если объем работы нетривиален, используйте отдельный слот вместо лямбда, чтобы сохранить читаемость.
Обратите внимание на отсутствие явного управления памятью. Использование указателей владения для классов Qt является преждевременной оптимизацией, и его следует избегать там, где это необходимо. Объекты могут быть прямыми членами Worker
(или его PIMPL).
Под-объекты должны быть частью иерархии владения, которая имеет Worker
в корне. Таким образом, вы можете безопасно переместить экземпляр Worker
в другой поток, а его объекты будут следовать за ним. Конечно, вы также можете создать экземпляр Worker
в правильной теме - для этого есть простой idiom. Диспетчер событий потока принадлежит рабочему, поэтому, когда цикл событий потока завершается (т. Е. После вызова QThread::quit()
), рабочий будет автоматически удален и никакие ресурсы не будут течь.
template <typename Obj>
void instantiateInThread(QThread * thread) {
Q_ASSERT(thread);
QObject * dispatcher = thread->eventDispatcher();
Q_ASSERT(dispatcher); // the thread must have an event loop
QTimer::singleShot(0, dispatcher, [dispatcher](){
// this happens in the given thread
new Obj(dispatcher);
});
}
Реализация работника:
class Worker : public QObject {
Q_OBJECT
QSslSocket sslSocket;
QTimer timer;
QStateMachine machine;
QState s1, s2, s3;
Q_SIGNAL void finished();
public:
explicit Worker(QObject * parent = {}) : QObject(parent),
sslSocket(this), timer(this), machine(this),
s1(&machine), s2(&machine), s3(&machine) {
timer.setSingleShot(true);
s1.addTransition(&sslSocket, SIGNAL(encrypted()), &s2);
s1.addTransition(&timer, SIGNAL(timeout()), &s3);
connect(&s1, &QState::entered, [this]{
// connect the socket here
...
timer.start(10000);
});
connect(&s2, &QState::entered, [this]{
// other_things here
...
// end other_things
emit finished();
});
machine.setInitialState(&s1);
machine.start();
}
};
Тогда:
void waitForEventDispatcher(QThread * thread) {
while (thread->isRunning() && !thread->eventDispatcher())
QThread::yieldCurrentThread();
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
struct _ : QThread { ~Thread() { quit(); wait(); } thread;
thread.start();
waitForEventDispatcher(&thread);
instantiateInThread<Worker>(&myThread);
...
return app.exec();
}
Обратите внимание, что подключение к QThread::started()
будет колоритный: диспетчеру событие не существует до некоторого кода в QThread::run()
имел шанс выполнить. Таким образом, нам нужно дождаться, когда поток будет достигнут, уступив - это, скорее всего, приведет к тому, что рабочий поток будет развиваться достаточно далеко в пределах одного или двух уроков. Таким образом, это не будет тратить много времени.
Очень хорошая идея. Я тестировал его (с некоторыми изменениями в соответствии с моим кодом и целью), и он работает нормально. Я проголосовал, конечно :) –