我正在修改一个多线程 Qt 应用程序并且遇到了无法预料的行为。
我有一个从 QThread 继承的 WorkerThread 类,其中的方法run()
在做一些工作。handleSuccess()
WorkerThread 对象的一个槽连接到来自另一个主线程的信号,该信号是响应来自服务器的异步传入连接而发出的。据我了解,在run()
运行流控制时不在exec()
事件循环中,无法调用插槽。相反,我在应用程序的日志中看到线程正在中间点完成它的工作,run()
然后立即进入插槽而不做任何准备。
class WorkerThread : public QThread {
Q_OBJECT
public:
WorkerThread() { moveToThread(this); /* further initialization */ }
void run();
void foo();
public slots:
void handleSuccess(const QByteArray &);
void shutdown();
private:
QMutex mutex;
MyResource resource;
volatile bool mShutdown;
/* ... other declarations */
};
void WorkerThread::foo() {
QMutexLocker locker(&mutex);
/* Working with the guarded resource */
}
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;
/* ... useful payload */
foo();
/* ... another payload */
}
}
void WorkerThread::handleSuccess(const QByteArray & data) {
log(Q_FUNC_INFO, __LINE__, __FILE__);
QMutexLocker locker(&mutex);
/* processing data */
}
void WorkerThread::shutdown()
{
mShutdown = true;
emit shutdownThread();
}
对象在主线程中创建和初始化:
void SomeClass::init() {
/* ... */
mWorker = new WorkerThread();
connect(connect(mProtocol, SIGNAL(dataReceived(QByteArray)), mWorker, SLOT(processSuccess(QByteArray)),
Qt::QueuedConnection));
mWorker->start();
/* ... */
}
foo()
当从内部调用时发生错误run()
,锁定互斥锁,然后流控制被传递给handleSuccess()
我没有任何可见的理由,最后,handleSuccess()
尝试锁定互斥锁导致死锁。我会强调所有这一切都发生在一个线程中,我已经记录了。事实是,误差是稳定的,每次都出现在同一个地方。很明显,我没有考虑到某些事情,但究竟是什么?
更新
我已经重写了WorkerThread
intoWorker: public QObject
并且前者run()
变成了 public slot handleNewArrivals()
,它在线程的事件循环中调用以响应信号。尽管如此,问题仍然存在:另一个插槽在handleNewArrivals()
. 中断的地方是稳定的,这里是(seek evaluateTo()
):
bool hasContent = false;
QByteArray outData;
QBuffer inputBuffer(&data), outputBuffer(&outData);
inputBuffer.open(QIODevice::ReadOnly);
outputBuffer.open(QIODevice::WriteOnly|QIODevice::Append);
QXmlQuery xmlQuery;
xmlQuery.bindVariable("inputDocument", &inputBuffer);
xmlQuery.setQuery("doc($inputDocument)/commands//*");
QXmlSerializer serializer(xmlQuery, &outputBuffer);
log(Q_FUNC_INFO, __LINE__, __FILE__);
// the log line above appears, then after 1 ms the line
// from handleSuccess() appears successively.
xmlQuery.evaluateTo(&serializer);
// this line is never shown
log(Q_FUNC_INFO, __LINE__, __FILE__);
QXmlStreamReader xmlReader(outData);
while (!xmlReader.atEnd()) {
xmlReader.readNext();
if (xmlReader.tokenType() == QXmlStreamReader::Invalid) {
ufo::logT(this) << tr("Invalid token: %1").arg(xmlReader.errorString());
continue;
}
if (!xmlReader.isStartDocument() && !xmlReader.isEndDocument()) {
xmlWriterDb.writeCurrentToken(xmlReader);
hasContent = true;
}
}
ufo::logT(this) << tr("Selected data from payment, hasContent = %1").arg(hasContent?"true":"false");
inputBuffer.close();
outputBuffer.close();
QXmlQuery::evaluateTo 和 QXmlSerializer 会导致这种跳转?似乎有一个异常或类似unix的信号(该软件在Windows下运行,mingw32)或其他东西,尽管包装evaluateto()
或try-catch中的整个槽体没有给出任何东西。