如何正确停止运行非循环QThread?
How to stop running non-loop QThread correctly?
简介
假设我有一个带有 GUI 的应用程序,它从用户那里收集一些数据,然后调用一个嵌入式 python 脚本。我想添加 "cancel button" 以防用户想要停止进程。
示例代码
mainwindow
#include "calc_script.h"
signals:
void stopWorkSignal();
private:
calc_script *sender;
private slots:
Calculating()
on_pushButton_Cancel_clicked()
void MainWindow::Calculating()
{
QThread* newThread = new QThread();
connect(newThread, &QThread::started,
[=]() { sender->transfer(val_1, val_2, val_3); });
connect(this,
SIGNAL(stopWorkSignal()),
newThread,
SLOT(deleteLater())
newThread->start();
}
void MainWindow::on_pushButton_Cancel_clicked()
{
emit stopWorkSignal();
qDebug() << "stopwork signal emmitted";
}
calc_script.cpp
void calc_script::transfer(double val_1, double val_2, double val_3)
{
///Here the python (from boost.python) is executed
while(1) {}//this loop will generate a load to mimic this script, you cannot edit it, as the communication with .py is one-side at this lvl
}
问题
当信号被调用时,我收到错误 QThread destroyed while thread is still running
(并且计算似乎仍在进行)。如果我通过 SLOT(quit())
,什么也不会发生。如果计算是简单的循环,我可以传递一个标志来中断循环。但是由于调用 python 脚本我无法做到这一点,所以我试图破坏保存计算的线程。执行所述功能的正确方法是什么?
PS。我知道我没有包含对 python 的整个调用,但它很长。对于重现错误,你可以在 transfer
函数中使用任何非循环长计算,它会做基本相同的情况。
你不能强行终止一个线程;您所能做的就是让它退出,然后等待它自行退出。 (确实存在 QThread::terminate() 方法,但您不应该在生产代码中使用它,因为它会导致问题:例如,如果线程在终止时锁定了一个互斥量,则该互斥量将保留永远锁定,您的程序将在下次尝试锁定该互斥锁时死锁并冻结。
所以你有两个选择:要么想办法让 Python 线程退出,要么使用 QProcess object (or something equivalent to it) to run the Python code in a child process instead of inside a thread. The benefit of running the Python code in a separate process is that you can safely kill() 子进程——因为子进程不共享任何声明您的 GUI 进程,OS 将自动清理子进程分配的任何资源,子进程保持互斥锁锁定或其他资源未释放没有问题。
如果您更愿意礼貌地请求 Python 线程(或进程)退出,而不是简单地敲打它,您可以通过网络接口这样做;例如,您可以在 GUI 代码和 Python 事件循环之间创建一个 TCP 连接,并且 Python 事件循环可以在其 TCP 连接的末端定期进行非阻塞读取。然后,当您的 GUI 希望 Python 循环退出时,GUI 可以关闭其 TCP 套接字,这将导致 Python 循环调用 read()
到 return 0(又名EOF),Python 循环知道它的意思是 "time to exit",因此它可以自动退出。
简介
假设我有一个带有 GUI 的应用程序,它从用户那里收集一些数据,然后调用一个嵌入式 python 脚本。我想添加 "cancel button" 以防用户想要停止进程。
示例代码
mainwindow
#include "calc_script.h"
signals:
void stopWorkSignal();
private:
calc_script *sender;
private slots:
Calculating()
on_pushButton_Cancel_clicked()
void MainWindow::Calculating()
{
QThread* newThread = new QThread();
connect(newThread, &QThread::started,
[=]() { sender->transfer(val_1, val_2, val_3); });
connect(this,
SIGNAL(stopWorkSignal()),
newThread,
SLOT(deleteLater())
newThread->start();
}
void MainWindow::on_pushButton_Cancel_clicked()
{
emit stopWorkSignal();
qDebug() << "stopwork signal emmitted";
}
calc_script.cpp
void calc_script::transfer(double val_1, double val_2, double val_3)
{
///Here the python (from boost.python) is executed
while(1) {}//this loop will generate a load to mimic this script, you cannot edit it, as the communication with .py is one-side at this lvl
}
问题
当信号被调用时,我收到错误 QThread destroyed while thread is still running
(并且计算似乎仍在进行)。如果我通过 SLOT(quit())
,什么也不会发生。如果计算是简单的循环,我可以传递一个标志来中断循环。但是由于调用 python 脚本我无法做到这一点,所以我试图破坏保存计算的线程。执行所述功能的正确方法是什么?
PS。我知道我没有包含对 python 的整个调用,但它很长。对于重现错误,你可以在 transfer
函数中使用任何非循环长计算,它会做基本相同的情况。
你不能强行终止一个线程;您所能做的就是让它退出,然后等待它自行退出。 (确实存在 QThread::terminate() 方法,但您不应该在生产代码中使用它,因为它会导致问题:例如,如果线程在终止时锁定了一个互斥量,则该互斥量将保留永远锁定,您的程序将在下次尝试锁定该互斥锁时死锁并冻结。
所以你有两个选择:要么想办法让 Python 线程退出,要么使用 QProcess object (or something equivalent to it) to run the Python code in a child process instead of inside a thread. The benefit of running the Python code in a separate process is that you can safely kill() 子进程——因为子进程不共享任何声明您的 GUI 进程,OS 将自动清理子进程分配的任何资源,子进程保持互斥锁锁定或其他资源未释放没有问题。
如果您更愿意礼貌地请求 Python 线程(或进程)退出,而不是简单地敲打它,您可以通过网络接口这样做;例如,您可以在 GUI 代码和 Python 事件循环之间创建一个 TCP 连接,并且 Python 事件循环可以在其 TCP 连接的末端定期进行非阻塞读取。然后,当您的 GUI 希望 Python 循环退出时,GUI 可以关闭其 TCP 套接字,这将导致 Python 循环调用 read()
到 return 0(又名EOF),Python 循环知道它的意思是 "time to exit",因此它可以自动退出。