Слоты внутри QThread работают одновременно

У меня есть два класса — обработчик и рабочий, связанные с сигналом и слотами. Вот упрощенная версия (псевдокод):

Конструктор обработчиков:

Handler::Handler(QObject *parent) : QObject(parent)
{
m_workerThread = new QThread;
m_worker = new Worker;
m_worker->moveToThread(m_workerThread);
m_workerThread->start();

connect(this, &Handler::doWork, m_worker, &Worker::doWork);
connect(this, &Handler::stopWork, m_worker, &Worker::stopWork);

emit doWork();
}

работник

void Worker::doWork()
{
qDebug()<<"------"qDebug()<<"Start do work executing in: "<<QThread::currentThreadId();
//doing some work
m_timer->start();//a singleshot timer that calls doWork() on timeout
qDebug()<<"work done";
qDebug()<<"------"
}

void Worker::stopWork()
{
qDebug()<<"Stop timer executing in: "<<QThread::currentThreadId();
m_timer->stop();
}

Таким образом, в основном работа начинается после выброса «doWork» из обработчика. В слоте «doWork» есть таймер одиночной съемки, который через определенное время снова вызывает ту же функцию.
Через некоторое время обработчик выдает сигнал «stopWork».
Вот мой вывод отладки:

------
Start do work executing in: 0x65602450
work done
------
------
Start do work executing in: 0x65602450
work done
------
------
Start do work executing in: 0x65602450
work done
------
------
Start do work executing in: 0x65602450
stop work emitted from handler in: 0x750a7000
Stop timer executing in: 0x65602450
work done
------
------
Start do work executing in: 0x65602450
work done
------
etc...

Так что я не понимаю, как это возможно, что мой рабочий поток выполняет два слота (doWork и stopWork) одновременно? Разве сигнал stopWork не должен публиковаться и ждать, пока поток не перейдет в режим ожидания, прежде чем выполнять слот stopWork?

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

Также из моих тестов я понял, что это происходит в 30-40% случаев.

1

Решение

Использование классов синхронизации потоков, таких как QMutex и / или QSemaphore, может решить вашу проблему.

Например, если вы создали QMutex в своем классе Handler и передали его в свой рабочий поток:

Handler::Handler(QObject *parent) : QObject(parent)
{
//Class variable : QMutex* mutex
mutex = new QMutex();
m_workerThread = new QThread;
m_worker = new Worker;
m_worker->moveToThread(m_workerThread);
m_workerThread->start();

connect(this, &Handler::doWork, m_worker, &Worker::doWork);
connect(this, &Handler::stopWork, m_worker, &Worker::stopWork,Qt::BlockingQueuedConnection);

emit doWork(mutex);
}

и затем вызвал механизм блокировки этого общего QMutex из другого потока:

void Worker::doWork(QMutex* mutex)
{
mutex->lock();
qDebug()<<"------"qDebug()<<"Start do work executing in: "<<QThread::currentThreadId();
//doing some work
m_timer->start();//a singleshot timer that calls doWork() on timeout
qDebug()<<"work done";
qDebug()<<"------"mutex->unlock();

}

И использовал ту же логику в вашем классе Handler, непосредственно перед тем, как вы подаете сигнал «стоп»:

...
mutex->lock();
emit stopWork();
mutex->unlock();
...

Таким образом, вы могли бы избежать нежелательного параллелизма.

Флаг BlockingQueuedConnection здесь гарантирует, что ваш основной поток не продвинется без завершения задания остановки, поэтому не разблокируйте мьютекс перед выполнением задания.

Это логика, которую я пытаюсь использовать при решении ситуаций параллелизма и гонок данных, надеюсь, это вам тоже поможет.

0

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

Других решений пока нет …

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