In der asynchronen Programmierung wird das "Warten auf" als Anti-Pattern betrachtet. Anstatt auf Dinge zu warten, sollten Sie den Code so gestalten, dass er darauf reagiert, dass eine Bedingung erfüllt wird. Z. B. verbinden Sie den Code mit einem Signal.
Eine Möglichkeit, dies zu implementieren, besteht darin, Ihre Aktionen in separate Status zu zerlegen und bei der Eingabe jedes Status einige Arbeit zu leisten. Natürlich, wenn die Menge der Arbeit nicht trivial ist, verwenden Sie einen separaten Steckplatz anstelle eines Lambda, um die Dinge lesbar zu halten.
Beachten Sie das Fehlen einer expliziten Speicherverwaltung. Die Verwendung von besitzenden Zeigern zu Qt-Klassen ist eine vorzeitige Optimierung und sollte vermieden werden, wo dies unnötig ist. Die Objekte können direkte Mitglieder des Worker
(oder seines PIMPL) sein.
Die Unterobjekte müssen alle Teil der Eigentümerhierarchie sein, die Worker
im Stammverzeichnis hat. Auf diese Weise können Sie die Worker
Instanz sicher in einen anderen Thread verschieben, und die Objekte, die sie verwendet, folgen ihr. Natürlich können Sie auch die Worker
in den richtigen Thread instanziieren - es gibt eine einfache idiom dafür. Der Ereignis-Dispatcher des Threads besitzt den Worker. Wenn die Ereignisschleife des Threads beendet wird (d. H. Nach dem Aufruf von QThread::quit()
), wird der Worker automatisch entfernt, und es werden keine Ressourcen verloren gehen.
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);
});
}
Die Arbeiter Umsetzung:
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();
}
};
Dann:
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();
}
Beachten Sie, dass wäre rassig zu QThread::started()
verbindet: der Event-Dispatcher existiert nicht erst einige Code innerhalb QThread::run()
hatte eine Chance zu erfüllen. Daher müssen wir darauf warten, dass der Thread dorthin gelangt, indem er nachgibt. Dies führt sehr wahrscheinlich dazu, dass der Worker-Thread innerhalb einer oder zwei Renditen weit genug fortschreitet. So wird es nicht viel Zeit verschwenden.
Sehr gute Idee. Ich habe es getestet (mit einigen Änderungen, die meinem Code und meinem Zweck entsprechen) und es funktioniert in Ordnung. Ich habe natürlich gewählt :) –