Добрый день, коллеги.
Занимаюсь модификацией кода приложения на Qt, и столкнулся с непредвиденным для меня поведением. Есть наследник класса QThread, который в методе run() выполняет полезную работу время от времени. Объект также подписан слотом handleSuccess() на сигнал от объекта из другого потока, который принимает асинхронные ответы с сервера по HTTP. В моем понимании, слот не должен вызываться посреди контекста потока, пока поток не войдет в цикл обработки сообщений. Однако, по какой-то причине это происходит. Далее код.
Класс WorkerThread -- выполняется как поток:
class WorkerThread : public QObject {
Q_OBJECT
public:
WorkerThread() { moveToThread(this); /* дальнейшая инициализация */ }
void run();
void foo();
public slots:
void handleSuccess(const QByteArray &);
private:
QMutex mutex;
MyResource resource;
/* ... другие объявления */
};
void WorkerThread::foo() {
QMutexLocker locker(&mutex);
/* Единоличная работа с защищаемым ресурсом */
}
void WorkerThread::run() {
QEventLoop signalWaiterEventLoop;
connect(this, SIGNAL(terminated()), &signalWaiterEventLoop, SLOT(quit()), Qt::QueuedConnection);
connect(this, SIGNAL(shutdownThread()), &signalWaiterEventLoop, SLOT(quit()), Qt::QueuedConnection);
connect(this, SIGNAL(dbChanged()), &signalWaiterEventLoop, SLOT(quit()), Qt::QueuedConnection);
while (!mShutdown) {
signalWaiterEventLoop.exec();
if (mShutdown) break;
/* ... полезная нагрузка */
foo();
/* ... полезная нагрузка */
}
}
void WorkerThread::handleSuccess(const QByteArray & data) {
QMutexLocker locker(&mutex);
/* обработка данных */
}
Объект создается и инициализируется в главном потоке приложения:
void SomeClass::init() {
/* ... */
mWorker = new WorkerThread();
connect(connect(mProtocol, SIGNAL(dataReceived(QByteArray)), mWorker, SLOT(processSuccess(QByteArray)),
Qt::QueuedConnection));
mWorker->start();
/* ... */
}
Ошибка возникает, когда метод foo() вызывается в run(), захватывает мьютекс, а затем выполнение прерывается методом handleSuccess() безо всяких видимых причин (логирование перед emit-ом сигнала dataReceived ничего не дает: в логах log4qt запись не появляется). Когда handleSuccess() захватывает мьютекс, программа получает дедлок.
Таким образом, возникает вопрос: что я делаю не так, либо чего я не учитываю?
Здравствуйте, SaZ, Вы писали:
SaZ>mShutdown — объявлена ли как volatile? меняется ли из разных потоков? Если да — то как синхронизируется?
Объявлена как volatile bool mShutdown, не синхронизируется непосредственно, изменяется через слот такого вида:
public slots:
void shutdown();
/* ... */
void WorkerThread::shutdown() {
mShutdown = true;
emit shutdownThread();
}