C++ 如何停止正确运行非循环QThread?

C++ 如何停止正确运行非循环QThread?,c++,qt,qthread,C++,Qt,Qthread,导言 假设我有一个带有GUI的应用程序,它从用户那里收集一些数据,然后调用一个嵌入式python脚本。我想添加“取消按钮”,以防用户想要停止进程 示例代码 main窗口 #include "calc_script.h" signals: void stopWorkSignal(); private: calc_script *sender; private slots: Calculating() on_pushButton_Cancel_clicked()

导言

假设我有一个带有GUI的应用程序,它从用户那里收集一些数据,然后调用一个嵌入式python脚本。我想添加“取消按钮”,以防用户想要停止进程

示例代码

main窗口

#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";
}
问题 当信号被调用时,我得到了错误
QThread在线程仍在运行时被销毁(并且计算似乎仍在进行)。如果我通过
插槽(quit())
,则不会发生任何事情。如果计算是简单的循环,我可以传递一个标志来阻止循环。但是由于调用python脚本,我无法做到这一点,所以我尝试销毁保存计算的线程。实现所述功能的正确方法是什么


另外,我知道我没有包括对python的全部调用,但它很长。对于复制错误,您可以在
transfer
函数中使用任何非循环长计算,它将基本上执行相同的情况。

您不能强制终止线程;你所能做的就是让它退出,然后等待它自动退出。(确实存在一个方法,但您不应该在生产代码中使用它,因为它会导致问题:例如,如果线程在终止时锁定了一个互斥体,该互斥体将永远保持锁定,并且您的程序在下次尝试锁定该互斥体时将死锁并冻结)

因此,您有两种选择:要么找出一种方法要求Python线程退出,要么使用一个对象(或与其等价的对象)在子进程中而不是在线程中运行Python代码。在单独的进程中运行Python代码的好处是,您可以安全地创建子进程——因为子进程不与GUI进程共享任何状态,并且操作系统将自动清理子进程分配的任何资源,子进程保持互斥锁锁定或其他资源未释放没有问题


如果您更愿意礼貌地要求Python线程(或进程)退出,而不是简单地敲打它,您可以通过网络接口这样做;例如,您可以在GUI代码和Python事件循环之间创建一个TCP连接,Python事件循环可以定期在TCP连接的末尾执行非阻塞读取。然后,当GUI希望Python循环退出时,GUI可以关闭其TCP套接字,这将导致Python循环对
read()
的调用返回0(也称为EOF),Python循环知道这意味着“退出时间”,因此它可以自动退出。

不能强制终止线程;你所能做的就是让它退出,然后等待它自动退出。(确实存在一个方法,但您不应该在生产代码中使用它,因为它会导致问题:例如,如果线程在终止时锁定了一个互斥体,该互斥体将永远保持锁定,并且您的程序在下次尝试锁定该互斥体时将死锁并冻结)

因此,您有两种选择:要么找出一种方法要求Python线程退出,要么使用一个对象(或与其等价的对象)在子进程中而不是在线程中运行Python代码。在单独的进程中运行Python代码的好处是,您可以安全地创建子进程——因为子进程不与GUI进程共享任何状态,并且操作系统将自动清理子进程分配的任何资源,子进程保持互斥锁锁定或其他资源未释放没有问题


如果您更愿意礼貌地要求Python线程(或进程)退出,而不是简单地敲打它,您可以通过网络接口这样做;例如,您可以在GUI代码和Python事件循环之间创建一个TCP连接,Python事件循环可以定期在TCP连接的末尾执行非阻塞读取。然后,当您的GUI希望Python循环退出时,GUI可以关闭其TCP套接字,这将导致Python循环对
read()
的调用返回0(也称为EOF),Python循环知道这意味着“退出时间”,因此它可以自动退出。

我描述了如何在没有Python的情况下复制它。如果您需要任何额外的数据,请不要犹豫,我会尽力提供。在传递函数中说非循环长计算是令人困惑的,因此最好明确地放置该代码,而不是您的原始代码,而是允许您重现问题的任何简单代码。此外,我认为可能有必要修改.py,如果您无法修改.py,那么最好清楚地指出它。我可以修改.py,但脚本没有与线程实时通信,因此可能不可能传递任何标志-至少在我看来是这样。如果您想复制它,只需在
transfer
(例如
while(1){}
)中放置任何无休止的循环,并将其视为此脚本(您无法修改它)。如果这对你来说还不够,请通知我,我会尝试为你找到其他解决方案。我根据您的建议编辑了这个问题。经过您的修改,python和boost是不相关的,因为在您的问题中,您没有显示与
while(true){}
while(1){}
不同的内容,您不能退出线程,而是终止它。或者您需要有
,而(!done){}
其中
done
是组织线程退出的条件。更不用说如此紧密的循环只是浪费了一个CPU头。我描述了如何在没有python的情况下复制它。如果您需要任何额外的数据,请不要犹豫,我会尽力提供。在传递函数中说非循环长计算是令人困惑的,所以最好明确地放置代码,而不是您
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
}