如何在长时间运行的方法中等待信号?

时间:2017-07-28 19:54:24

标签: c++ multithreading qt signals bison

目前我正在开发一种基于flex& bison的简单编程语言。

为此,我首先构建抽象语法树(AST)并继续其评估。在评估期间,应该可以接收用户输入。用户输入在Qt-GUI中完成。 ast-evaluation-procedure在一个由GUI创建的thread-worker-object中运行。 我的问题:哪一个是最好的方式,以及#34;阻止"然后运行flex& Bison评估程序从GUI接收用户输入,然后继续执行程序? 不幸的是,我不知道如何将评估程序分成两部分来捕获来自GUI的用户输入信号,然后继续使用"拆分程序"在一个插槽中。 我的当前解决方案(在我看来很差)在评估过程中使用无限循环,直到线程worker-object中的布尔成员变量(_hasUserInput)设置为true。在用户输入之后,GUI发出信号。相应的槽位于线程工作者对象内。该插槽接收用户输入,例如作为QString并设置相应的成员变量。此外,布尔变量设置为true,因此可以保留评估过程的无限循环,可以处理用户输入并继续评估。

功能原理:
GUI - >启动线程(工作者对象) - >启动长期运行程序

伪代码中的示例:

class ThreadWorker : public QObject {

    ...

    // Slot, that reacts to the signal from the GUI
    void userInput(const QString &input) {
        _userInput = input;
        _hasUserInput = true;
    }

    // evaluation-procedure
    void doEvaluation() {
        // evaluation of the AST

        ...

        emit userInputRequired();
        while (!hasUserInput){          // Check if user input has been done
            qApp -> processEvents();    // to react to events
        }
        do something with user input;
        continue valuation;             // Processing of user input and continuation of the procedure
    }

    ...

};

我试着尽可能好地描述我的问题并希望它有效。如果您需要更详细的信息,请询问。 非常感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

由于您已经将工作人员分解为QObject,因此您永远不需要致电processEvents()。相反,将AST评估分解为只需要执行的小块。我还使公共接口成为线程安全的 - 因此您可以直接从任何线程调用它。

// see https://stackoverflow.com/a/40382821/1329652
bool isSafe(QObject * obj);
template <typename Class, typename... Args> void postCall(Class * obj, void (Class::*method)(Args...), Args&& ...args);

class ASTWorker : public QObject {
  Q_OBJECT
  QBasicTimer m_evaluateTimer; // a bit lower overhead than QTimer
  void timerEvent(QTimerEvent * ev) override {
    if (ev->timerId() == m_evaluateTimer.timerId())
      evaluateImpl();
  }
  void evaluateImpl() {
    bool evaluationDone = {};
    QThread::msleep(10); // emulate evaluating a chunk of AST
    if (!evaluationDone && !m_evaluateTimer.isActive())
      m_evaluateTimer.start(0, this);
    else if (evaluationDone)
      m_evaluateTimer.stop();
  }
public:
  using QObject::QObject;
  /// Thread-safe
  Q_SLOT void setUserInput(const Data & data) {
    if (!isSafe(this))
      return postCall(this, &ASTWorker::setUserInput, data);
    QThread::msleep(1); // emulate processing user data
  }
  /// Thread-safe
  Q_SLOT void evaluate() {
    if (!isSafe(this))
      return postCall(this, &ASTWorker::evaluate, data);
    evaluateImpl();
  }
};