Прерывание потока во время выполнения
От: andrey-x86  
Дата: 10.06.13 13:08
Оценка:
Добрый день, коллеги.
Занимаюсь модификацией кода приложения на 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() захватывает мьютекс, программа получает дедлок.

Таким образом, возникает вопрос: что я делаю не так, либо чего я не учитываю?
c++ qt threading
Re: Прерывание потока во время выполнения
От: SaZ  
Дата: 10.06.13 14:07
Оценка:
Здравствуйте, andrey-x86, Вы писали:

AX>Добрый день, коллеги.

AX>...
AX>Таким образом, возникает вопрос: что я делаю не так, либо чего я не учитываю?

mShutdown — объявлена ли как volatile? меняется ли из разных потоков? Если да — то как синхронизируется?
Re[2]: Прерывание потока во время выполнения
От: Аноним  
Дата: 10.06.13 17:51
Оценка:
Здравствуйте, SaZ, Вы писали:

SaZ>mShutdown — объявлена ли как volatile? меняется ли из разных потоков? Если да — то как синхронизируется?


Объявлена как volatile bool mShutdown, не синхронизируется непосредственно, изменяется через слот такого вида:


public slots:
    void shutdown();
/* ... */

void WorkerThread::shutdown() {
    mShutdown = true;
    emit shutdownThread();
}
Re: Прерывание потока во время выполнения
От: Karbofos Россия  
Дата: 11.06.13 10:29
Оценка:
Здравствуйте, andrey-x86, Вы писали:

AX>Таким образом, возникает вопрос: что я делаю не так, либо чего я не учитываю?


Такое поведение обычно возникает когда запускается вложенный EventLoop.
Посмотрите, нет ли у вас в коде foo() или в вызываемых им функциях явного вызова QEventLoop::exec() либо метода XXX::processEvents(), где XXX = QEventLoop, QApplication или QCoreApplication. Скорее всего что-то подобное найдется.
Вообще, лучше избегать вложенных EventLoop. Но если это действительно необходимо, то проблему дедлока можно обойти создав mutex с параметром RecursionMode = Recursive. Однако, при этом можно получить несколько других проблем.
Re[2]: Прерывание потока во время выполнения
От: andrey-x86  
Дата: 11.06.13 13:13
Оценка:
Здравствуйте, Karbofos, Вы писали:

K>Здравствуйте, andrey-x86, Вы писали:


AX>>Таким образом, возникает вопрос: что я делаю не так, либо чего я не учитываю?


K>Такое поведение обычно возникает когда запускается вложенный EventLoop.

K>Посмотрите, нет ли у вас в коде foo() или в вызываемых им функциях явного вызова QEventLoop::exec() либо метода XXX::processEvents(), где XXX = QEventLoop, QApplication или QCoreApplication. Скорее всего что-то подобное найдется.
K>Вообще, лучше избегать вложенных EventLoop. Но если это действительно необходимо, то проблему дедлока можно обойти создав mutex с параметром RecursionMode = Recursive. Однако, при этом можно получить несколько других проблем.

Действительно, так и оказалось. QXmlQuery запускает внутри одного метода event loop, и появляются эти глюки. Обходное решение -- сгрузить этот кусочек работы в отдельный поток, который уже не будет получать события, предназначенные для исходного. После этого все заработало, как надо.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.