QEventLoop и async/await

311
12 апреля 2017, 15:40

В своем проекте пытаюсь реализовать подобие механизма async/await операций на Qt c использование QEventLoop. Приведу простейший пример окна с одной кнопкой, по нажатию которой должен вызывать код, не блокирующий основной (GUI) поток.

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QPushButton *button = new QPushButton("button",this);
    connect(button,&QPushButton::clicked,this,&MainWindow::onClicked);
}
void MainWindow::onClicked()
{
    qDebug() << time() << " onClicked start ";
    QEventLoop loop;
    QtConcurrent::run([&loop]() {
        qDebug() << time() << " Thread start: " << (void*)QThread::currentThread();
        //doSomething
        QThread::currentThread()->sleep(10);
        QMetaObject::invokeMethod(&loop,"quit",Qt::QueuedConnection)
        qDebug() << time() << " Thread finish: " << (void*)QThread::currentThread();
    });
    loop.exec();
    qDebug() << time() << " onClicked finish ";
}

При однократном нажатии все замечательно, QtConcurrent запустит задачу, которая выполнится в отдельном потоке, а loop.exec() остановит выполнение функции, но не заблокирует EventLoop всего приложения, окно будет активно, кнопку можно будет нажать еще. В отладчике будет выведено, примерно следующее:

16:57:08.774  onClicked start 
16:57:08.775  Thread start:  0xa83230
16:57:18.775  Thread finish:  0xa83230
16:57:18.775  onClicked finish 

Если нажать кнопку несколько раз, то слот вызовется несколько раз и при каждом вызове создадутся объекты QEventLoop, которые остановят выполнение слота в ожидании окончания асинхронных задач, что очевидно. НО Выход из цикла обработки событий у этих объектов произойдет одновременно по окончанию последней асинхронной задачи, т.е. после вызова последнего слота quit()
В отладчике следующее:

17:26:28.464  onClicked start 
17:26:28.464  Thread start:  0xa83230
17:26:30.988  onClicked start 
17:26:30.988  Thread start:  0x8dae20
17:26:37.549  onClicked start 
17:26:37.549  Thread start:  0xa349c0
17:26:38.464  Thread finish:  0xa83230
17:26:40.989  Thread finish:  0x8dae20
17:26:47.549  Thread finish:  0xa349c0
17:26:47.549  onClicked finish 
17:26:47.549  onClicked finish 
17:26:47.549  onClicked finish 

Вопрос: почему при создании нескольких объектов QEventLoop, они заканчивают свои циклы обработки только после вызова слота quit() каждого из объектов и как это изменить?

Answer 1

В Qt используется приватный мьютекс у объекта потока (QThread) с атомарным счётчиком ссылок (QAtomicInt):

// qeventloop.cpp, стр. 164
QMutexLocker locker(&static_cast<QThreadPrivate *>
    (QObjectPrivate::get(d->threadData->thread))->mutex);

Соответственно, если поток, в котором существуют экземпляры QEventLoop, один и тот же, то покуда счётчик ссылок quitLockRef (см. файл qeventloop_p.h) не уйдёт в ноль, каждый инстанс QEventLoop будет бесконечно крутиться в собственном цикле.

Для организации асинхронной реакции на завершение работы QtConcurrent лучше всего подходит QFutureWatcher:

void MainWindow::onClicked() {
    qDebug() << time() << " onClicked start ";
    QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);
    connect(watcher, &QFutureWatcher<void>::finished
        , this, [this,watcher]() {
        qDebug() << time() << " onClicked finish ";
        watcher->deleteLater();
    });
    QFuture<void> future
        = QtConcurrent::run([this]() {
            qDebug() << time() << " Thread start: "
                << (void*)QThread::currentThread();
            //doSomething
            QThread::currentThread()->sleep(10);
            qDebug() << time() << " Thread finish: "
                << (void*)QThread::currentThread();
    });
    watcher->setFuture(future);
}
READ ALSO
разработать класс для создания файла c++ [требует правки]

разработать класс для создания файла c++ [требует правки]

Разработать метод-член класса для нового файла, содержащего

251
Где можно скачать натренированный word2vec? [требует правки]

Где можно скачать натренированный word2vec? [требует правки]

Доброго дня! Есть где-нибудь в доступе готовый натренированный, например на twitter или дрсоцсетях, файл word2vec с векторами?

297
как получить адрес поля структуры?

как получить адрес поля структуры?

Написал программу на вывод чисел в байтах и адреса хранения этих байтов,но преподаватель говорит,что адреса полей хранятся в сложных типах...

351
Парсер данных в кольцевом буфере

Парсер данных в кольцевом буфере

Программирую STM32 на C\C++, имеется кольцевой буфер входящих данных (не важно, текстовых или бинарных)Естественно известен формат данных, которые...

206