Как разгрузить вычислительно сложную задачу, которая сильно зависит от правильной обработки исключений?

В моем приложении Qt мне приходится выполнять некоторые сложные вычислительные задачи, обычно загружая огромные наборы данных с диска (однопоточные). Код в задаче в значительной степени зависит от правильной обработки исключений и строго не является Qt.
Я разгрузить эти задачи из потока GUI, используя QtConcurrent::run(), так что я могу показать индикатор выполнения. Однако, как я понял из документации Qt, я не могу перехватывать исключения в потоке GUI. Я понимаю, что теоретически это не имеет смысла. Однако на практике я использую потоки только из-за индикатора выполнения. Как лучше всего справиться с этой ситуацией?

Редактировать: Я хотел бы подчеркнуть, что мне не нужен детальный контроль над загруженной работой. Я действительно только разгрузить его, чтобы показать индикатор выполнения. Это, вероятно, ситуация, с которой столкнулись и другие.

2

Решение

Вы можете обернуть всю свою функцию в один try / catch. При обнаружении исключения вы можете уведомить поток GUI, используя пользовательский сигнал.

Также вы можете запустить свою функцию в потоке GUI и вызвать QApplication::processEvents() периодически обновлять пользовательский интерфейс (индикаторы выполнения и т. д.).

1

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

Я заставил это работать сейчас. Вместо звонка 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 и затем перехватывается циклом событий основного потока.

1

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

Вы должны также рассмотреть, какой поток является ведущим, а какой следующим. Вы уверены, что хотите, чтобы поток графического интерфейса управлял потоком, который читает данные? Не могли бы вы иметь поток, который загружает данные, просто обрабатывать свои собственные исключения и сообщать о своем прогрессе потоку графического интерфейса? Почему поток gui является центральным / управляющим / основным потоком?

Извините, я не могу предложить больше советов, но удачи в вашей программе 🙂

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