У вас есть две проблемы:
Когда приложение убивается с помощью Ctrl-C, оно получает сигнал Kill или Abort от Os и завершается как можно скорее.Поэтому никакие деструкторы или сигналы aboutToQuit не будут запущены вследствие прерывания обычного разматывания стека.
Ваш рабочий класс не обрабатывает прерывания.Обычно у функции запуска есть какой-то цикл, который повторяет работу с некоторым фрагментом данных.Чтобы корректно остановить QRunnable, вы должны выйти из функции запуска.Вы можете сделать это безопасно, используя переменную-член std :: atomic boolean, чтобы разорвать цикл на класс MyWorker, и использовать signal / slots для его переключения.
Вот исправление для проблемы 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» - это атомарная переменная, гарантирующая, что к ней доступен потокобезопасный.(Atomics - это функция c ++ 11)
Устанавливается в false после того, как сигнал workerStopRequested (см. Main.cpp) и слот abort () выполнен, как следствие соединения, выполненного вконструктор класса MyTimer.
Обратите внимание, что при прерывании прерывания цикл обработки прекратится на следующей итерации.Это означает, что вы можете увидеть не более 8 точек, напечатанных на экране после «остановленной» строки (по одной для каждой нити в соответствии с максимальным количеством нитей).