我正在编写一个简单的控制台应用程序,该应用程序使用QThreadPool生成了一些工作程序。我正在使用Qt 5.12.2和Microsoft Visual C ++编译器14.0(amd64)在Windows 10上进行编译。需要使用计时器以固定的时间间隔开始工作,但是该工作要花费比该时间间隔更长的时间。 8个线程应该能够跟上工作量。我启动了该应用程序,它运行得非常顺利且符合预期。
当我想退出命令行应用程序时,我按ctrl-c停止。该应用程序然后挂起。计时器停止并且所有输出停止,但是它不会使我返回命令提示符。我必须打开任务管理器才能退出该应用程序。我敢肯定这与QThreadPool不能正确清理有关,但是我找不到如何清理它。感谢您的建议。
我尝试从计时器捕获破坏信号,并从应用程序捕获aboutToQuit信号。都没有人开火。如果您注释掉线程池的开始,则应用程序将正确退出。我还在MyTimer类中将线程池创建为成员变量,并将工作线程转换为指针。所有变化都会导致相同的结果,请在退出时挂起。
我在这台计算机上没有调试器可以连接并查看应用程序挂起的位置。
mytimer.h
#ifndef MYTIMER_H
#define MYTIMER_H
#include <QDebug>
#include <QTimer>
#include <QThreadPool>
class MyWorker : public QRunnable
{
public:
void run()
{
QThread::msleep(150); //simulate some work
qDebug() << ".";
}
};
class MyTimer : public QObject
{
Q_OBJECT
public:
MyTimer()
{
QThreadPool::globalInstance()->setMaxThreadCount(8);
worker.setAutoDelete(false);
// setup signal and slot
connect(&timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
timer.setTimerType(Qt::PreciseTimer);
// msec
timer.start(50);
}
QTimer timer;
MyWorker worker;
public slots:
void MyTimerSlot()
{
//Comment the below line and the ctrl-c will work.
QThreadPool::globalInstance()->start(&worker);
qDebug() << "-";
}
};
#endif // MYTIMER_H
main.cpp
#include <QCoreApplication>
#include "mytimer.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyTimer timer;
return a.exec();
}
我希望当我在执行应用程序时按ctrl-c时,应用程序将干净退出,并将控制权返回给命令提示符。
答案 0 :(得分:0)
您在这里有两个问题:
使用Ctrl-C终止应用程序时,它将收到来自OS的Kill或Abort信号,并尽快终止。因此,由于正常堆栈展开的中断,不会触发任何析构函数或aboutToQuit信号。
您的工作者类不处理中断。通常,运行函数具有某种循环,可对某些数据块进行迭代。为了正常停止QRunnable,您必须退出运行功能。您可以使用std :: atomic布尔成员变量将循环分成MyWorker类,并使用信号/插槽对其进行切换,从而安全地将其线程化。
这是问题1的解决方法:
#include <QCoreApplication>
#include <csignal>
#include "mytimer.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyTimer timer;
QObject::connect(qApp, &QCoreApplication::aboutToQuit, &timer, &MyTimer::workerStopRequested);
signal(SIGTERM, [](int sig) { qApp->quit(); });
signal(SIGABRT, [](int sig) { qApp->quit(); });
signal(SIGINT, [](int sig) { qApp->quit(); });
signal(SIGKILL, [](int sig){ qApp->quit(); });
return a.exec();
}
使用csignal头文件中提供的实用程序,您可以捕获终止信号并强制应用程序退出,并触发aboutToQuit信号。
此信号还用于告诉MyTimer实例停止其工作者触发workerStopRequested信号,这是问题2的解决方案的一部分:
#ifndef MYTIMER_H
#define MYTIMER_H
#include <QDebug>
#include <QTimer>
#include <QThreadPool>
class MyWorker : public QObject, public QRunnable
{
Q_OBJECT
public:
explicit MyWorker(QObject* parent = nullptr) :
QObject(parent),
QRunnable()
{
aborted = false;
}
void run()
{
while (!aborted)
{
QThread::msleep(150); //simulate some work
qDebug() << ".";
}
}
public slots:
void abort()
{
aborted = true;
qDebug() << "stopped.";
}
protected:
std::atomic<bool> aborted;
};
class MyTimer : public QObject
{
Q_OBJECT
public:
MyTimer()
{
QThreadPool::globalInstance()->setMaxThreadCount(8);
worker.setAutoDelete(false); // <-- Good. Worker is not a simple QRunnable anymore
// setup signal and slot
connect(&timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
connect(this, &MyTimer::workerStopRequested, &worker, &MyWorker::abort);
timer.setTimerType(Qt::PreciseTimer);
// msec
timer.start(50);
}
QTimer timer;
MyWorker worker;
signals:
void workerStopRequested();
public slots:
void MyTimerSlot()
{
//Comment the below line and the ctrl-c will work.
QThreadPool::globalInstance()->start(&worker);
qDebug() << "-";
}
};
#endif // MYTIMER_H
MyWorker类从QObject继承来利用信号/插槽从运行函数处理循环中退出。布尔值“ aborted”是一个原子变量,以确保可以线程安全地对其进行访问。 (原子是c ++ 11的功能)
由于在MyTimer类的构造函数中建立了连接,因此在发出workerStopRequested信号(请参阅main.cpp)并执行abort()插槽之后,将其设置为false。
请注意,当切换“中止”时,它将导致处理循环在下一次迭代时停止。这意味着在“停止”字符串之后,您最多可以在屏幕上看到8个点(根据最大线程数,每个线程一个)。