Бесконтактный цикл должен быть циклом событий, а затем он может автоматически обрабатывать вызовы слотов поперечного сечения, не беспокоясь о деталях.
Идиома для запуска кода «непрерывно» в цикле событий - это таймер с нулевой продолжительностью.
Допустим, вы начинаете с кодом, который выглядит следующим образом:
class MyThread : public QThread {
bool button_is_clicked_flag = false;
void run() override {
forever{
if (button_is_clicked_flag) {
onButtonClick();
button_is_clicked_flag = false;
}
doWork();
}
}
void onButtonClick();
void doWork();
public:
using QThread::QThread;
void setButtonClickedFlag();
}
int main(int argc, char **argv) {
...
MyThread t;
t.start();
...
}
Он необходим для doWork()
не слишком долго - и не слишком коротким. Если на современном оборудовании потребовалось ~ 5 мс, это было бы практически правильным компромиссом между накладными расходами и задержками для приложения общего назначения. Если вам нужна более низкая реакция на задержку в рабочем потоке, тогда doWork()
должен выполнять меньше работы. Вероятно, для doWork()
это не имеет смысла, чтобы взять гораздо меньше 1 мс.
И всякий раз, когда doWork()
не имеет ничего общего, например. если это сделано с вычислением, которое оно должно было выполнить, оно должно остановить таймер, который сохраняет его в живых.
Вы должны преобразовать его выглядеть следующим образом:
class MyWorker : public QObject {
Q_OBJECT
QBasicTimer m_timer;
void doWork();
void timerEvent(QTimerEvent *event) {
if (event->timerId() == m_timer.timerId())
doWork();
}
public:
explicit MyWorker(QObject *parent = nullptr) : QObject(parent) {
m_timer.start(0, this);
}
Q_SLOT void onButtonClick() {
// Ensure we're invoked correctly
Q_ASSERT(QThread::currentThread() == thread());
...
}
}
class Window : public QWidget {
Ui::Window ui;
public:
Q_SIGNAL void buttonClicked();
explicit Window(QWidget *parent = nullptr) : QWidget(parent) {
ui.setupUi(this);
connect(ui.button, &QPushButton::clicked, this, &Window::buttonClicked);
}
};
class SafeThread : public QThread {
Q_OBJECT
using QThread::run; // final method
public:
~SafeThread() { quit(); wait(); } // we're safe to destroy - always
};
int main(int argc, char **argv) {
...
MyWorker worker;
SafeThread thread;
Window window;
// onButtonClick will be executed in worker->thread()
connect(&window, &Window::buttonClicked, &worker, &MyWorker::onButtonClick);
worker.moveToThread(&thread);
thread.start();
window.show();
return app.exec();
}
Петля событие, которое проходит в QThread::run
будет постоянно вызывать doWork
через обработчик событий таймера. Но всякий раз, когда требуется перекрестный вызов слота для объекта, живущего в этом потоке, цикл события будет предоставлять внутренний QMetaCallEvent
, представляющий вызов слота, до QObject::event
, который затем выполнит вызов.
Таким образом, когда вы устанавливаете точку останова в onButtonClick
, в стеке вызовов будет QObject::event
.
Читайте о потоках в документации Qt. Ваш вопрос не создает впечатления, что вы приложили много усилий для решения проблемы самостоятельно. – Silicomancer
@Silicomancer Почему вы так думаете? Разве это не одно правильное решение? Я думаю, вы не понимаете, чего я пытаюсь достичь здесь ... – carobnodrvo