2016-12-04 3 views
0

Возможно ли реализовать прерывание функции в Qt (5.x).Прерывание пользовательских функций

Например, если у меня есть кнопка и хотите что-то выполнить на резьбе (который работает бесконечный цикл) при нажатии на эту кнопку, я мог бы сказать что-то вроде этого:

in thread... 
forever 
{ 
    if(button_is_pressed_flag) 
    { 
    do something... 
    } 
} 

есть лучше путь?

+0

Читайте о потоках в документации Qt. Ваш вопрос не создает впечатления, что вы приложили много усилий для решения проблемы самостоятельно. – Silicomancer

+0

@Silicomancer Почему вы так думаете? Разве это не одно правильное решение? Я думаю, вы не понимаете, чего я пытаюсь достичь здесь ... – carobnodrvo

ответ

3

Бесконтактный цикл должен быть циклом событий, а затем он может автоматически обрабатывать вызовы слотов поперечного сечения, не беспокоясь о деталях.

Идиома для запуска кода «непрерывно» в цикле событий - это таймер с нулевой продолжительностью.

Допустим, вы начинаете с кодом, который выглядит следующим образом:

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.

+0

Один вопрос: не могли бы вы объяснить мне, почему вы используете 'QBasicTimer m_timer;'. Я просто не понимаю, почему он там с 0 таймаутом. – carobnodrvo

+1

«QBasicTimer» - это просто обтекатель RAII вокруг идентификатора таймера, который вы могли бы получить из 'QObject :: startTimer'. «StartTimer» - это C-like API. Поскольку мы используем C++ по какой-либо причине (а не для написания глупого кода), вместо этого следует использовать 'QBasicTimer'. Нулевой тайм-аут - особое значение. Это совсем не связано со временем. Это означает: «сигнализировать тайм-аут каждый раз, когда очередь событий сливается». Это заставляет цикл событий никогда не блокировать ожидания новых событий: он постоянно сигнализирует о тайм-ауте. Это похоже на запуск собственного цикла 'forever', который также истощает очередь событий. –

+0

Спасибо! Если бы я мог принять ваш комментарий, а :) – carobnodrvo

1

Вы можете начать поток, а затем сразу же ждать по std :: condition_variable, а затем, когда кнопка нажата (событие, вызываемое в основном потоке), сообщите об этой переменной условия и поток проснется.

Однако это немного странно. Что ты пытаешься сделать? вызвать асинхронную задачу при нажатии кнопки? В этом случае, возможно, было бы лучше просто начать с события нажатия кнопки с помощью std :: packaged_task или std :: async.

+0

То, что я пытаюсь достичь здесь, это следующее: этот поток читает большой текстовый файл (журнал), анализирует его и излучает сигналы на основе того, что делает он анализирует. Проблема здесь в том, что я должен уметь приостанавливать этот поток, останавливать его, позволять ему анализировать «быстрее» (меньше спать между шагами) и т. Д. Поэтому переменная условия здесь не так удобна. – carobnodrvo

+0

Вы можете общаться между потоками с флагами, вам просто нужно читать/писать им внутри заблокированного мьютекса – gbehar

+0

Я знаю, но что, если у меня есть, скажем 100 флагов.Это не будет работать так хорошо, потому что всегда будет небольшая задержка между ними. – carobnodrvo

Смежные вопросы