QProcess не убивает / не завершает процесс, если он определен в куче

Я хочу убить / завершить процесс, который я создал при выходе из приложения:

int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QPushButton w; w.show();
struct Lambda {
static void run() {
static QProcess p; //version 1
//            QProcess& p = *new QProcess(qApp); //version 2
p.connect(qApp, &QApplication::aboutToQuit, &p, &QProcess::kill);
p.connect(qApp, &QApplication::aboutToQuit, &p, &QProcess::terminate);
p.connect(qApp, &QApplication::aboutToQuit, &p, &QProcess::close);
p.connect(qApp, &QApplication::aboutToQuit, &p, &QProcess::deleteLater);
p.start("caffeinate -d");
}
};
QtConcurrent::run(Lambda::run);
return a.exec();
}

С версией 1: мое приложение запускается так, как я ожидаю: создать и уничтожить процесс успешно, но при выходе из приложения QCreator сообщает: «QProcess: уничтожено, пока процесс (« caffeinate ») все еще работает».

С версией 2: мое приложение может запустить процесс, но не может завершить или завершить процесс при выходе, и нет отчетов, как указано выше.

Я просто хочу спросить, почему при создании в куче, QProcess не может быть убит как стационарная версия? Спасибо!

(Я использовал struct Lambda, потому что я не могу использовать C ++ 11 лямбда в моем проекте)

2

Решение

ТЛ; др

В обоих случаях сигналы не доставляются; в первом случае деструктор убивает процесс, во втором он даже не имеет возможности запустить.

В общем, ваш код представляет собой хороший сборник практически всех бездушных QObject, QThread, сигналы и т.п .; читать Потоки и объекты QObject прежде чем делать что-либо с потоками, объектами QObject и сигналами в Qt. Это важная информация, без которой вы будете только делать беспорядок, как это. * Также этот Статья в вики дает хорошее изложение «правильного способа» использовать потоки с Qt.


Детальное объяснение

Давайте назовем основной поток нить А и тема началась QtConcurrent::run резьба B.

Случай 1

когда run запускается из второго потока, p создан, поэтому он имеет родство нитей с резьбой В. По этой причине все connect Вы выступаете на нем соединения в очереди (по умолчанию для connect является AutoConnection, который использует QueuedConnection если связанные объекты имеют различное сродство потока — и qApp создан в нить А).

Проблема в том, что соединения в очереди работают, только если в принимающем потоке работает цикл обработки событий (они реализованы как sendEvent, так что если в целевом потоке нет событий обработки цикла, они только накапливаются в очереди событий), тогда как здесь run возвращается сразу после запуска процесса.

Так, kill, terminate, close а также deleteLater никогда не называются. Заметить, что:

  • призвание deleteLater в этом случае все равно было бы ошибкой, так как он будет пытаться сделать delete на static объект;
  • ни kill ни terminate являются синхронными, поэтому, чтобы убедиться, что процесс мертв, прежде чем продолжить, вам нужно было бы также waitForFinished;
  • также, возможно, нить, которая была закручена QtConcurrent::run будет мертвым после run завершаясь1; это определенно плохо, потому что вы будете иметь QObjects лежит вокруг с привязанностью нити к нити, которая мертва. Я не знаю, как изящно sendEvent обрабатывает эту ситуацию.

Во всяком случае, когда программа заканчивается, pдеструктор вызывается автоматически как обычная часть завершения работы приложения C ++2; как задокументировано, деструктор QProcess завершает процесс, с которым он связан, если он все еще работает (но также записывает «страшное сообщение», которое вы видели).

Дело 2

Как и в случае 1, вы создаете QProcess с резьба B сродство; так что все, что мы сказали выше о событиях не доставляются & сотрудничество. все еще применяется.

Здесь есть три основных различия:

  • вы устанавливаете родителя p в qApp, который живет в главном потоке; это явно запрещено, все родительско-дочерние отношения между объектами QObject должны существовать между объектами с одинаковой привязкой к потоку; вероятно, вы получаете какое-то предупреждение в консоли об этом факте (setParent Явно проверяю, живут ли объекты в одном потоке QObjectконструктор делает то же самое);
  • в этом случае deleteLater могло бы быть уместным (если бы у вас был цикл обработки событий), так как вы выделяли new;
  • но самое главное, здесь pдеструктор никогда не вызывается, так как он был наделен new и никто не звонит delete в теме; по этой причине запущенный процесс продолжает работать (также у вас небольшая утечка памяти).

Итак, что было бы правильным способом справиться с этим? Лично я бы вообще избежал потоков и сигналов. Запуск процесса уже асинхронный, так что вы могли бы просто сделать:

int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QProcess p;
p.start("caffeinate -d");
QPushButton w; w.show();
int ret = a.exec();
p.close();
return ret;
}

как всегда с потоками, очередями событий, сигналами и тому подобным: не усложняйте, чем нужно.


Сноски

  1. На практике в этом случае вы, вероятно, не заметите, потому что QtConcurrent использует глобальный пул потоков, который убивает закрученные потоки только после 30 секунд простоя.

  2. Общий совет: обычно вы не хотите, чтобы «сложные» объекты уничтожались таким образом, так как main уже завершено, поэтому (1) это усложняет отладку и (2) если у вас есть объекты Qt, которые зависят от QApplication еще жив (как правило, все в QtGui и QtWidgets) вы начнете получать странные сбои при завершении программы.

3

Другие решения


По вопросам рекламы [email protected]