控制在另一个线程中工作的QObject,绕过事件队列,冻结GUI

时间:2012-02-03 15:50:40

标签: qt signals-slots qthread

我正在使用QThread和插槽/信号机制;我知道在Web上有一个很多的线程,特别是在SO,但我仍然找不到解决方案。无论如何,这是上下文。

我想要提出的代码片段旨在通过GUI控制最终的漫长过程,因此使用了QThread

我有一个带有两个按钮的窗口,启动和停止。我的Window也有QThreadTask,其中后者继承自QObject。我希望能够在任务运行时停止我的任务,并且如果在任务已经运行时单击了启动,则阻止再次启动它。

以下是Task的摘录(假设漫长的过程):

class Task: public QObject
{
public:

  Task(): QObject(), stop_(true) {}

private slots:

  void startTask()
  {
    stop_ = false;
    run();
  }

  void stopTask()
  {
    stop_ = true;
  }

  void run() const
  {
    while ( ! stop_)
    {
      sleep(1);
    }
  }

  bool stop_;
};

我在Window的构造函数中的按钮和任务之间建立了两个连接:

class Window: public QWidget
{
public:

  Window()
  {
    // Instantiate buttons and put them in a layout.
    // ...

    connect(buttonStart_, SIGNAL(clicked()), &task_, SLOT(startTask()));
    connect(buttonStop_, SIGNAL(clicked()), &task_, SLOT(stopTask()),
            Qt::DirectConnection);

    task_.moveToThread(&thread);
    thread_.start();
  }

private:
  QPushButton buttonStart_;
  QPushButton buttonStop_;

  QThread thread_;
  Task task_;
};

我在第二个Qt::DirectConnection中使用了connect(),以“强制”处理我的信号请求停止任务,因为(据我所知)task_需要从其返回在进一步处理事件之前工作(如果我使用默认连接,则在我的任务完成后处理所有点击)。

这里,Qt::DirectConnection“绕过”事件队列,这样我可以停止我的任务。但说实话,我不知道这是否是正确的方法,或者它是否是一种解决方法(因此可能是我的问题的根源)。

无论如何,这样就行了,但是当我开始玩我的按钮时,GUI最终会被冻结,这就是我的问题!

欢迎任何帮助;谢谢你的时间!

2 个答案:

答案 0 :(得分:4)

使用DirectConnection意味着将在UI线程中执行插槽(当您的任务线程仍在运行时)。这不是你想要的。处理此问题的方法是在线程中使用事件循环,方法是使用QThread::exec()

运行

为了让您的线程能够根据需要进行响应,您需要确保线程能够处理传入的事件。有几种方法可以解决这个问题。一个是在你的任务运行时偶尔调用QCoreApplication::processEvents()。另一种方法是使用连接到执行某些处理的插槽的QTimer。重要的是确保线程中的事件循环可以运行。

答案 1 :(得分:2)

您需要了解的第一件事是执行信号/插槽连接的线程。

在这种特殊情况下的默认行为是使用Qt::QueuedConnection,它会将您的信号事件放到接收线程的事件队列中。在Qt::DirectConnection的情况下,插槽在发出信号的对象的线程中执行

如果你在stop_变量周围放置一个互斥锁,以防止两个线程同时访问它,那么在这种情况下使用Qt :: DirectConnection是安全的。