【问题标题】:Kill a QProcess running in a different QThread杀死在不同 QThread 中运行的 QProcess
【发布时间】:2017-10-13 15:36:26
【问题描述】:

我有一个继承QThread 的类,我重新实现了run() 方法。在这个 run 方法中,有一个 QProcess(在 run 中声明)启动一个程序。

现在,如果我在该进程仍在运行时关闭我的应用程序,它实际上不会在进程完成之前关闭。

所以我的问题是如何停止这个过程?

根据我在 Qt 文档中阅读的内容,我不能在此处使用信号/插槽,因为我的进程将与不是主线程的线程具有线程亲和性,因此连接已排队,这不会停止我的进程.

我使用的是 Qt 5.8

提前致谢!

编辑:这是线程代码,如果您需要更多信息,请告诉我

void run()
{
    QString cmd;
    QProcess p;

    connect(&p, SIGNAL(finished(int)), this, SLOT(quit()));
    //connect(this, SIGNAL(signalTerminateProcess()), &p, SLOT(kill())); // queued connect that doesn't kill my process

    p.start(cmd);
    if(p.waitForFinished(-1))
    {
        qInfo("process done");
    }
}

【问题讨论】:

  • 显示线程代码。
  • 如果可以简单的使用QProcess信号,为什么还要使用线程来执行进程?
  • 因为我的run函数里面有两种情况,其中一种不能用QProcess完成,但是我决定把这两个部分都保留在这个QThread类中
  • 你可以试试 connect(this,SIGNAL(finished()), &p , SLOT(close()))
  • 使用您显示的代码,如果您停止进程,则线程将终止。使用 QProcess::kill()

标签: c++ multithreading qt qthread qprocess


【解决方案1】:

首先,您可以跨 QThreads 使用信号和槽,甚至可以使用Qt::BlockingQueuedConnection 等待槽执行。见Qt documentation

但是,这要求目标 QObject 存在于具有正在运行的事件循环的线程中。在您的情况下,由于您已重载 QThread::run(),您将不会有事件循环。

在我看来,您有 2 种方法来修复您的代码,要么进行小修复,要么更改设计并使用事件循环。

1。快速修复

void run()
{
    QString cmd;
    QProcess p;

    connect(&p, SIGNAL(finished(int)), this, SLOT(quit()));
    //connect(this, SIGNAL(signalTerminateProcess()), &p, SLOT(kill())); // queued connect that doesn't kill my process

    p.start(cmd);
    while(! p.waitForFinished(100)) //Wake up every 100ms and check if we must exit
    {
        if (QThread::currentThread()->isInterruptionRequested())
        {
            p.terminate();
            if (! p.waitForFinished(1000))
                p.kill()
            break;
        }
    }
    qInfo("process done");
}

int cleanUp()
{
    thread->requestInterruption();
    thread->wait();
}

QProcess *process = nullptr;
void run()
{
    QString cmd;
    QProcess p;
    process = &p; //You shouldn't do that in real code

    connect(&p, SIGNAL(finished(int)), this, SLOT(quit()));
    //connect(this, SIGNAL(signalTerminateProcess()), &p, SLOT(kill())); // queued connect that doesn't kill my process

    p.start(cmd);
    while(! p.waitForFinished(100)) //Wake up every 100ms and check if we must exit
    {
        QCoreApplication::processEvents();
    }
    qInfo("process done");
    process = nullptr;
}

int cleanUp()
{
    QTimer::singleShot(0, process, &QProcess::terminate);
    // Qt 5.10 -> QMetaObject::invokeMethod(process, &QProcess::terminate);
    if (! thread->wait(1000))
    {
        QTimer::singleShot(0, process, &QProcess::kill);
        thread->wait();
    }
}

2。事件循环修复

// Create thread
QThread thread;
thread.start();

// Create process and move it to the other thread
QProcess process;
process.moveToThread(&thread);

void startProcess()
{
    p.start(cmd);
}

QMutex mutex;
void stopProcess()
{
    p.terminate();
    if (!p.waitForFinished(1000))
        p.kill();
    p.moveToThread(qApp->thread());
    mutex.unlock();
}

// Call startProcess() in the other thread
QTimer::singleShot(0, &process, &startProcess);

// Call stopProcess() in the other thread
mutex.lock();
QTimer::singleShot(0, &process, &stopProcess);
mutex.lock();

thread.quit();
if (!thread.wait(100))
    thread.terminate();

你可以:

  • 使用原子或使用与Qt::BlockingQueuedConnection 连接的信号替换互斥锁。
  • startProcess()stopProcess() 替换为插槽和 lambda。
  • 将调用QTimer::singleShot() 替换为QMetaObject::invokeMethod() 或使用信号。 请注意,您可以将Qt::BlockingQueuedConnectionQMetaObject::invokeMethod() 一起使用

【讨论】:

  • 嗨,本杰明,你的第一次修复,效果很好!不知何故,谷歌总是向我展示 Qt 4.8 的 QThread 文档页面,而 requestInterruption() 那时还没有,所以我错过了。