В моем приложении Qt мне приходится выполнять некоторые сложные вычислительные задачи, обычно загружая огромные наборы данных с диска (однопоточные). Код в задаче в значительной степени зависит от правильной обработки исключений и строго не является Qt.
Я разгрузить эти задачи из потока GUI, используя QtConcurrent::run()
, так что я могу показать индикатор выполнения. Однако, как я понял из документации Qt, я не могу перехватывать исключения в потоке GUI. Я понимаю, что теоретически это не имеет смысла. Однако на практике я использую потоки только из-за индикатора выполнения. Как лучше всего справиться с этой ситуацией?
Редактировать: Я хотел бы подчеркнуть, что мне не нужен детальный контроль над загруженной работой. Я действительно только разгрузить его, чтобы показать индикатор выполнения. Это, вероятно, ситуация, с которой столкнулись и другие.
Вы можете обернуть всю свою функцию в один try / catch. При обнаружении исключения вы можете уведомить поток GUI, используя пользовательский сигнал.
Также вы можете запустить свою функцию в потоке GUI и вызвать QApplication::processEvents()
периодически обновлять пользовательский интерфейс (индикаторы выполнения и т. д.).
Я заставил это работать сейчас. Вместо звонка QtConcurrent::run
напрямую я использую обертку Qt, которая может перехватывать исключения, не относящиеся к Qt. Когда исключение перехватывается, оно передается в основной поток в соединение с очередями и перебрасывается.
namespace MyConcurrent {
template <class U, class V>
QFuture<U> offload(V func)
{
U (*caller)(V) = offloaded_placeholder<U>;
return QtConcurrent::run(caller, func);
}
template <class U, class V>
U offloaded_placeholder(V func)
{
try
{
return func();
}
catch(Utilities::ExceptionBase& e)
{
qRegisterMetaType<Utilities::ExceptionBase>("Utilities::ExceptionBase");
QMetaObject::invokeMethod(ConcurrencyCommunicator::getInstance(), "onRequestPushExceptionToMainThread", Qt::QueuedConnection, Q_ARG(const Utilities::ExceptionBase&, e));
}
}
}
Использование:
QFuture<ret_type> = MyConcurrent::offload<ret_type>(std::bind(&some_func, params));
Обратите внимание, что Utilities::ExceptionBase
это класс, производный от стандартных исключений. ConcurrencyCommunicator
простой класс, на который в GUI есть указатель Выдвигаемое исключение перебрасывается внутрь ConcurrencyCommunicator
и затем перехватывается циклом событий основного потока.
Я собираюсь сказать заранее, что я не использовал Qt.
При этом, если вы можете, вы должны попытаться разделить переменные между потоками. Используйте несколько глобальных переменных или указателей для передачи состояния одного потока другому (не забывайте, что параллелизм является потенциальной проблемой).
Вы должны также рассмотреть, какой поток является ведущим, а какой следующим. Вы уверены, что хотите, чтобы поток графического интерфейса управлял потоком, который читает данные? Не могли бы вы иметь поток, который загружает данные, просто обрабатывать свои собственные исключения и сообщать о своем прогрессе потоку графического интерфейса? Почему поток gui является центральным / управляющим / основным потоком?
Извините, я не могу предложить больше советов, но удачи в вашей программе 🙂