我正在使用QThread
和插槽/信号机制;我知道在Web上有一个很多的线程,特别是在SO,但我仍然找不到解决方案。无论如何,这是上下文。
我想要提出的代码片段旨在通过GUI控制最终的漫长过程,因此使用了QThread
。
我有一个带有两个按钮的窗口,启动和停止。我的Window
也有QThread
和Task
,其中后者继承自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最终会被冻结,这就是我的问题!
欢迎任何帮助;谢谢你的时间!
答案 0 :(得分:4)
使用DirectConnection
意味着将在UI线程中执行插槽(当您的任务线程仍在运行时)。这不是你想要的。处理此问题的方法是在线程中使用事件循环,方法是使用QThread::exec()
。
为了让您的线程能够根据需要进行响应,您需要确保线程能够处理传入的事件。有几种方法可以解决这个问题。一个是在你的任务运行时偶尔调用QCoreApplication::processEvents()
。另一种方法是使用连接到执行某些处理的插槽的QTimer
。重要的是确保线程中的事件循环可以运行。
答案 1 :(得分:2)
您需要了解的第一件事是执行信号/插槽连接的线程。
在这种特殊情况下的默认行为是使用Qt::QueuedConnection
,它会将您的信号事件放到接收线程的事件队列中。在Qt::DirectConnection
的情况下,插槽在发出信号的对象的线程中执行 。
如果你在stop_变量周围放置一个互斥锁,以防止两个线程同时访问它,那么在这种情况下使用Qt :: DirectConnection是安全的。