#c #multithreading #qt
#c #многопоточность #qt
Вопрос:
У меня есть основной класс, который запускает поток, выполняющий одно действие. Я пытаюсь уловить, когда завершаются операции с потоком.
Основной класс:
// .h
class MainClass : public QObject
{
Q_OBJECT
public:
QThread thread;
// ...
public slots:
void onFinish();
}
// .cpp
void MainClass::startThread()
{
thread = new QThread();
worker = new Worker();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
// connect(worker, SIGNAL(finished()), this, SLOT(onFinish()), Qt::DirectConnection);
}
void MainClass::onFinish()
{
std::cout << "Finished!" << std::endl << std::flush;
}
Worker
Класс для потока:
// .h
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
~Worker();
public slots:
void process();
signals:
void finished();
}
// .cpp
void Worker::process()
{
// ...Do stuff...
emit finished();
}
Выполнение startThread
не выводит «Готово!». Почему?
Я заметил, что если я добавляю Qt::DirectConnection
в строку, которая подключается к onFinish()
(например, в строке с комментариями), печатается сообщение. Но что я могу сделать, если я хочу выполнить onFinish()
действия в потоке основного класса?
Редактировать 1
Кроме того, кажется, что finished()
-> quit()
connect также не работает, потому что, если я вызываю thread->isFinished()
или thread->isRunning()
, после сна в основном потоке, чтобы убедиться, что задача потока завершена, я получаю false
и true
, соответственно.
Редактировать 2
Поскольку это также может иметь значение, вот main.cpp:
int _tmain(int argc, _TCHAR* argv[])
{
QCoreApplication a(argc, argv);
std::unique_ptr <MainClass> mc = std::make_unique <MainClass>();
mc->startThread();
mc->thread->wait();
return a.exec();
}
Комментарии:
1. Вы пробовали без подключения к
deleteLater()
слотам? Если он запускает вашonFinish()
слот, вы можете вызватьdeleteLater()
оба объекта оттуда.2. Вы можете подумать о том, чтобы поставить
connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
перед подключением кdeleteLater()
слотам.3. Имеет ли поток, в котором
MainClass
создается экземпляр, активный цикл событий? Если нет, то сигналы в очереди приниматься не будут.4. @LoPiTaL, @mfreiholz: Я попробовал ваши предложения. Сообщение все еще не напечатано. Также смотрите мою правку об
isFinished()
иisRunning()
. Похоже, проблема не связанаdeleteLater()
с.5. @G.M. Я создаю экземпляр
MainClass
в своем «main.cpp «, после запуска aQCoreApplication
. Означает ли это, что у меня есть цикл событий, который предотвращает получение сигналов?
Ответ №1:
На первый взгляд я бы сказал, что порядок соединений важен. deleteLater()
Слоты всегда должны быть последними методами.
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
Sitenode — QRunnable
Вместо этого вы можете использовать QRunnable, который предназначен для коротких задач / заданий. Вы можете объединить их с QObject или QFuture / QFutureWatcher, чтобы получать уведомления, как только они будут завершены.
Обновление 1
Ваш цикл основных событий никогда не запускается, и похоже, что вы хотите выйти из приложения после завершения задачи. Я не думаю, что это хорошая реализация, но эти модификации должны работать для вас:
int _tmain(int argc, _TCHAR* argv[])
{
QCoreApplication a(argc, argv);
std::unique_ptr<MainClass> mc = std::make_unique<MainClass>();
mc->startThread();
// do not wait!
// let the main-event-loop handle events -> a.exec()
// and quit() application by signal/slot
return a.exec();
}
void MainClass::startThread()
{
thread = new QThread();
worker = new Worker();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), qApp, SLOT(quit()));
}
Комментарии:
1. Спасибо. Я решил свою проблему, хотя до сих пор не уверен на 100%, в чем была проблема… Я думаю, что G.M. был прав насчет активного цикла событий, препятствующего выполнению слотов, поэтому проблема заключалась не в порядке «подключения». Я изучил вашу идею использования
QRunnable
и столкнулся с аналогичной проблемой. Наконец, я нашел решение сQFuture
помощью иQtConcurrent::run
. Но я не могу понять, почему последнее решение может сработать, если другие этого не сделали…