【问题标题】:How to stop running non-loop QThread correctly?如何正确停止运行非循环 QThread?
【发布时间】:2020-05-05 08:05:09
【问题描述】:

简介

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

示例代码

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 函数中使用任何非循环长计算,它的情况基本相同。

【问题讨论】:

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

标签: c++ qt qthread


【解决方案1】:

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

所以你有两个选择:要么想办法让 Python 线程退出,要么使用QProcess 对象(或与之等效的对象)在子进程中而不是在线程内运行 Python 代码.在单独的进程中运行 Python 代码的好处是您可以安全地kill() 子进程——因为子进程不与您的 GUI 进程共享任何状态,并且操作系统将自动清理由子进程,子进程将互斥锁锁定或其他资源未释放没有问题。

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

【讨论】:

  • 好的,从提出的解决方案来看,最优雅的方法似乎是退出 Python 解释器。但是如何在 boost.python 包中做到这一点?解释器由Py_Initialize(); 初始化。有没有终止命令?或者也许我应该这样做?
  • 让你的 python 脚本调用 sys.exit()。它需要从 Python 线程中调用。
  • sys.exit() 在我的情况下会干扰 QTimer,并使应用程序崩溃,但我可以使用 return。但是,当按下 c++ 中的按钮时,我不知道如何在 python 中调用它。如何在 python 中执行内部调用,从外部 c++ 代码。
  • 我的建议,正如我在回答中所说,是让 c++ 程序接受来自 python 线程的 tcp 连接,并在您希望 python 线程返回时关闭 tcp 连接。 python 线程可以检测到它的连接何时关闭,并使用该检测作为触发器立即返回。
  • 不知道 boost 是怎么做到的,但是当我想从我的嵌入式 python 脚本中调用一个 C 函数时,我需要让我的 C 函数返回一个 Python 对象(例如return PyLong_FromLong(5),如emb_numargs() 函数在嵌入式 Python 文档第 1.4 节中的示例中,位于 docs.python.org/3/extending/embedding.html)
猜你喜欢
  • 1970-01-01
  • 2021-06-11
  • 1970-01-01
  • 2013-07-29
  • 2015-05-17
  • 1970-01-01
  • 2020-11-23
  • 2020-09-02
  • 2015-02-27
相关资源
最近更新 更多