Qt5中C++程序非Qt线程与Qt网络通信的警告问题
Qt5中QSslSocket跨线程访问导致QSocketNotifier警告的问题
基于Qt5开发的C++程序,通过QSslSocket与TCP/IP服务器通信,功能正常但持续弹出警告:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
程序连接流程
主线程中发起服务器连接:
devConnectionHandler::instance().connect( args[0].sval, args[1].sval ) // 内部调用Qt库方法 devResult status = dev_connect( name.c_str(), interface.c_str(), NULL );
dev_connect实现:
Q_UNUSED(reserved); // 持有服务器通信套接字的QObject DevHalRemote *hal = new DevHalRemote(); // 执行实际连接 const DevResult result = hal->DevConnect(name, interface); if (result == DEV_OK) { m_RemoteList.insert(name, hal); } else { delete hal; } return result;
DevHalRemote构造函数(创建QSslSocket):
DevHalRemote::DevHalRemote(QObject *parent) : QObject(parent) { m_socket = new QSslSocket(this); m_socket->setProtocol(QSecureSslProtocols); m_socket->setPeerVerifyMode(QSslSocket::VerifyNone); m_socket->setProxy(QNetworkProxy::NoProxy); if (!QCoreApplication::instance()) new QCoreApplication(g_argc, g_argv); }
主线程数据访问逻辑
主线程初始化数据结构时调用:
DevItemProperty item = dev_getItemProperty( name, object )
dev_getItemProperty实现:
devItemProperty dev_getItemProperty(const char *name, const char *object) { DevHalRemote *hal = m_halRemoteList.value(name); return hal->GetItemProperty(object); }
DevHalRemote中数据读写调用链:
DevItemProperty DevHalRemote::GetItemProperty(const QByteArray &item) { const QByteArray data = writeRead(DEV_REMOTE_GETPROPERTY, item); if (data.isEmpty()) return DEV_ERROR; DevItemProperty result; memcpy(&result, data.constData(), sizeof(DevItemProperty)); return result; } QByteArray DevHalRemote::writeRead(char command, const QByteArray &value) const { QByteArray write = command; if (!value.isEmpty()) write += ';' + value; const QByteArray read = writeReadSynchronous(write); return read; } QByteArray DevHalRemote::writeReadSynchronous(const QByteArray &data) const { m_socket->write(data); if (!m_socket->waitForBytesWritten(timeout())) return QByteArray(); m_socket->waitForReadyRead(timeout()); const QByteArray read = m_socket->readAll(); QCoreApplication::instance()->processEvents(); return read; }
问题触发点:自定义pthread的跨线程访问
程序创建独立pthread监听Web界面修改,其run()方法中循环调用数据访问接口:
void devHalThread::run() { while( true ) { // devHal_info_t是自定义数据结构的typedef std::list<devHal_info_t*>::iterator it = _recs.begin(); if( _pause > 0. ) this->thread.sleep( _pause ); if( !_run ) continue; // 性能统计 struct timespec start; #ifdef _POSIX_CPUTIME clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &start ); #else clock_gettime( CLOCK_MONOTONIX, &start ); #endif //printf( "DevHalThread::run: Reading item '%s'\n", (*it)->object ); for( ; it != _recs.end(); ++it ) { /* 此调用触发QtSocketNotifier警告*/ DevItemProperty item = dev_getItemProperty( (*it)->interface, (*it)->object); if( strcmp( item.quality, Dev_ITEM_QUALITY_OK ) != 0 ) continue; // DevHAL中值已更新 memcpy( (*it)->value, item.value, VALUE_SIZE ); (*it)->time = time; callbackRequest( (*it)->pcallback ); } } }
问题核心:该pthread并非Qt线程,直接调用会在套接字创建线程(主线程)外访问QSslSocket,触发跨线程操作Qt对象的警告。
尝试的解决方案及新问题
尝试用QMetaObject::invokeMethod切换到Qt线程调用:
- 给
DevHalRemote添加槽函数:
Q_SLOT DevItemProperty devhal_getItem(const QString s); DevItemProperty devhal_getItem(const QString object){ DevItemProperty item = GetItemProperty(object.toUtf8()); return item; }
- 修改pthread中的调用逻辑:
QString object((*it)->object); QMetaObject::invokeMethod(halCon.hal_interface, "devhal_getItem", Qt::DirectConnection, Q_RETURN_ARG(DevItemProperty, item), Q_ARG(QString, object) );
- 使用
Qt::DirectConnection:调用可执行,但警告仍存在; - 使用
Qt::QueuedConnection:线程冻结,无数据更新。
需要找到消除警告同时保证功能正常的解决方案。
内容的提问来源于stack exchange,提问作者Yann Stephen
相关产品推荐
相关产品推荐

