Индивидуальное создание QFuture - PullRequest
1 голос
/ 17 февраля 2020

Я столкнулся с довольно странной проблемой с QtConcurrent, в основном из-за странных желаний программирования, может быть, это просто проблема XY, но ...

Итак, мой код пытается общаться с базой данных, на самом деле код бэкэнда (на Qt, да). Он должен работать быстро и обрабатывать некоторые запросы, поэтому мне нужен пул потоков. Как общеизвестный факт, я предполагаю, что установление соединения само по себе является очень трудоемкой операцией, поэтому существует необходимость в постоянных соединениях с базой данных, приводящих к постоянным потокам (QSqlDatabase нельзя перемещать между потоками). Также вполне естественно хотеть обрабатывать асинхронные запросы, в результате чего возникает необходимость в простом способе их передачи постоянным потокам.

Ничего слишком сложного, допустим, что в такой форме уже есть какой-то шаблон. ..


// That's what I want for now
QFuture<int> res = workers[i]->async(param1, param2);

// OR

// That's what I DO NOT want to get
workers[i]->async(param1, param2, [](QFuture<int> res) { // QFuture to pass exceptions
     // callback here
});

Это можно сделать наверняка. Почему не std::future? Что ж, гораздо проще использовать QFutureWatcher и это сигналы для уведомлений о готовности результата. Решения для уведомлений на чистом C ++ намного более сложные, и обратные вызовы также являются чем-то, что необходимо перетаскивать через иерархию классов. Очевидно, что каждый рабочий связывает поток с соединениями с БД.

Хорошо, все это можно написать, но ... пользовательский пул потоков будет означать отсутствие удобства QtConcurrent, кажется, существуют только рискованные способы создания что QFuture чтобы его мог вернуть таможенный работник. QThreadPool бесполезен, потому что было бы целой большой историей создать постоянные исполняемые объекты в нем. Другими словами, шаблон, который я кратко описал, станет неким ядром проекта, который используется во многих местах, а не чем-то, что можно легко заменить на 100 ручных операций с нитями.

Короче говоря, если Я мог бы подсказать QFuture за мои результаты, проблема будет решена. Может ли кто-нибудь указать мне на решение или обходной путь? Буду благодарен за любые яркие идеи.


UPD:

@ Владимир Бершов предложил хорошее современное решение, которое реализует паттерн наблюдателя. После некоторого поиска я нашел библиотеку QPromise . Конечно, создание пользовательского QFuture все еще хакерское и может быть сделано только через недокументированный класс QFutureInterface, но все же какое-то "обещающее" решение делает асинхронные вызовы более точными, насколько я могу судить.

Ответы [ 2 ]

1 голос
/ 19 февраля 2020

Вы можете использовать AsyncFuture библиотеку в качестве пользовательского QFuture инструмента создания или источника идей:

AsyncFuture - использовать QFuture как объект Promise

QFuture используется вместе с QtConcurrent для представления результата асинхронного вычисления. Это мощный компонент для многопоточного программирования. Но его использование ограничено результатом потоков, он не работает с асинхронным сигналом, испускаемым QObject. И это немного затрудняет настройку функции слушателя через QFutureWatcher.

AsyncFuture предназначен для улучшения функции, чтобы предложить лучший способ ее использования для асинхронного программирования. Он предоставляет объект Promise, такой как интерфейс. Этот проект вдохновлен AsynQt и Rx Cpp.

Особенности:

  • Преобразование сигнала из QObject в объект QFuture
  • Объединение нескольких фьючерсов с разными типами в один объект будущего
  • Использование Future в качестве объекта Promise
  • Chainable Callback - расширенная модель многопоточного программирования

Преобразование сигнала из QObject в объект QFuture:

#include "asyncfuture.h"
using namespace AsyncFuture;

// Convert a signal from QObject into a QFuture object

QFuture<void> future = observe(timer, &QTimer::timeout).future();

/* Listen from the future without using QFutureWatcher<T>*/
observe(future).subscribe([]() {
    // onCompleted. It is invoked when the observed future is finished successfully
    qDebug() << "onCompleted";
},[]() {
    // onCanceled
    qDebug() << "onCancel";
});
0 голосов
/ 18 февраля 2020

Моя идея состоит в том, чтобы использовать пулы потоков с максимум 1 доступным потоком для каждого.

QThreadPool* persistentThread = new QThreadPool; // no need to write custom thread pool
persistentThread->setMaxThreadCount(1);
persistentThread->setExpiryTimeout(-1);

, а затем

QFuture<int> future_1 = QtConcurrent::run(persistentThread, func_1);
QFuture<int> future_2 = QtConcurrent::run(persistentThread, func_2);

func_2 будет выполнено после func_1 в та же самая "постоянная" нить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...