Ошибка сигнализации в слотах qt - PullRequest
2 голосов
/ 26 сентября 2010

У меня есть объект «продюсер», который просматривает некоторые данные и испускает различные сигналы в зависимости от того, какой элемент данных следующий в очереди.

Каждый из этих сигналов обрабатывается одновременно не более чем одним объектом-потребителем (необходимо отсоединить его слоты перед подключением сигналов производителя к другому потребителю).

Если по какой-либо причине обработка на стороне потребителя завершается сбоем, обработка очереди должна быть остановлена, так как нет смысла продолжать ее.

Каков оптимальный способ сообщить производителю, что дальнейшая обработка не требуется из-за исключительного условия? Так как у потребителей есть указатель на производителя, я мог бы предположить, что может быть несколько способов, просто я не уверен, что они безопасны для условий гонки (не знаю, могу ли я зависеть от заказа какие сигналы испускаются / обрабатываются).

Два способа мыслить:

  • устанавливает флаг производителя, который можно проверить на следующей итерации, чтобы он знал, что пора останавливаться
  • регистрирует сигнал на потребителе (и соответствующий слот на производителе), который будет излучаться при остановке обработки

Мне интересно, являются ли эти решения жизнеспособными вообще в следующих сценариях:

  • производитель и потребитель (и) принадлежат к одному потоку
  • производитель и потребитель (и) принадлежат к разным потокам

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

Как вы, наверное, догадались, я пока не очень знаком с идиомами Qt для таких вещей.

Ответы [ 2 ]

2 голосов
/ 26 сентября 2010

У вас есть пара взаимосвязанных вопросов.

Для меня самый важный вопрос касается сигналов / слотов, работающих с потоками.

Когда используются сигналы / слоты в одном потоке, Qt по умолчанию предполагает AutoConnection или "прямое" соединение.В режиме прямого подключения сигнал / слоты действуют почти так же, как функция обратного вызова.Это означает, что функция, излучающая сигнал, по существу выполняет вызов подпрограммы.

При передаче сигнала / слотов по потокам Qt по умолчанию предполагает QueuedConnection по умолчанию.Что здесь происходит, то сложно.Последовательность -

  1. Излучаемый сигнал принимается QApplicationCore.
  2. Ядро создает глубокую копию аргументов в пространстве данных потока приема слота.
  3. Управление возвращается к функции, которая испустила сигнал.
  4. Ядро вызывает слот, когда может, основываясь на очереди событий.

Итак, зная это, вернемся к исходному вопросу: как узнать, когда функция слота прекратила обработку?У Qt нет идиомы, что я знаю, как передать эту информацию обратно.Но идиома с сигналом / слотами Qt состоит в том, что поток сигнализации не должен ничего знать о том, как функционирует слот или каков тип соединения.

Поэтому я рекомендую передавать данные через указатель на данныебыть обработанным.В данные я бы добавил два поля -

  1. Флаг результата.Как минимум, это должно включать состояния «Не начато», «Завершено», «Завершено с ошибками» и «Функция прервана».Флаг «Прерванная функция» будет установлен в блоке catch блока try / catch.
  2. Поле сторожевого таймера, основанное на QDateTime , установленное на текущую дату / время, когда излучается сигнал.Если поток сигнализации использует это значение, чтобы определить, полностью ли произошел сбой потока-потребителя.

Использование этого подхода - - Нет никаких причин, по которым вызывающий поток должен иметь какие-либо прямые знания о сигнальном потоке.- В этой структуре нет ничего, что нужно менять, если вы переходите с однопоточного или многопоточного.

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

1 голос
/ 26 сентября 2010

Идиома не является специфичной для qt.Я бы попросил вариант второй возможности, который вы уже предложили.Однако вам не нужно регистрировать пару сигнал / слот для ответа, просто передайте обратный вызов, который будет обработан производителем, но, возможно, в потоке потребителя.Например:

    // this answer might arrive on Consumer's thread...
    void Producer::ProcessAnswer(bool pShouldStop) {
        // mShouldStopProcessing is shared among threads
        if (mShouldStopProcessing) return;
        if (pShouldStop) {
            // double checking pattern...
            if (!mShouldStopProcessing) {
                Lock lock;
                if (!mShouldStopProcessing) {
                    // this notifies producer to stop processing
                    mShouldStopProcessing = true;
                }
            }
        }
    }

    void Producer::ProcessData() {
        for (DataContainer::iterator tCurrent = mData.begin();
            tCurrent != mData.end();
            ++tCurrent) {
                if (mShouldStopProcessing) break;
                else {
                    // emit signal here:
                    OnDataProcessing
                        (*tCurrent, std::bind(std::mem_fn(&Producer::ProcessAnswer), this));
                }
        }
    }

На стороне потребителя вам потребуется:

void ProcessData(Data& pData, std::function<void (bool)> pCallback) {
    // process data here...
    bool tResult = //...; 
    pCallback(tResult);
}
...