您的位置:首页 > 编程语言 > Qt开发

Qt:解决跨线程调用socket/IO类,导致报错的问题(socket notifiers cannot be enabled from another thread)

2018-01-10 11:17 5221 查看
Qt有很多IO相关的类,比如说QTcpSocket、QFile,总的来说,在Qt的框架内使用,还是非常方便的。

但是用过其他框架IO类的人,可能有一个很不习惯,就是Qt的所有IO类,都不推荐或者不可以跨线程操作,不然就会报错,比如说操作QTcpSocket跨线程调用write接口,就会报错:

socket notifiers cannot be enabled from another thread


要解决这个问题,直观的说就是不要跨线程操作,网上也有很多类似的说明。

这也是有道理的,很多时候真的是设计问题导致的,因为设计失误出现了不应该有的跨线程操作。

当然也可以用信号和槽封装一下,但是这样会涉及很多不必要的代码,我个人觉得也太过于麻烦。

那么我这里就提供一个更简单的方法,对QTcpSocket跨线程调用代码如下:

QMetaObject::invokeMethod( &socket, std::bind( static_cast< qint64(QTcpSocket::*)(const QByteArray &) >( &QTcpSocket::write ), &socket, QByteArray( "xxxx" ) ) );


这个比
socket->write( QByteArray( "xxxx" ) );
看起来麻烦很多,不过也算是一行代码搞定了。

来分析一下这个
invokeMethod
调用,接口的定义是这样的

bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr)


context:表示被调用的函数要在 哪个对象 的生存线程运行

function:被调用的函数

主要看这两个,后面都有缺省值,不用管。

在本例中context指定socket,就表示在socket的生存线程运行,这可能是任何线程,取决于你在哪里实例化这个socket。如果填写qApp,就表示指定在主线程运行。

function被赋值了一个std:bind,这是因为write不是槽函数,使用起来还是有点麻烦,不能直接写名字走moc系统。所以要手动用sid::bind把函数给包起来。

关于这个std::bind的3部分:

std::bind( static_cast< qint64(QTcpSocket::*)(const QByteArray &) >( &QTcpSocket::write )
,
&socket
,
QByteArray( "xxxx" ) )


static_cast< qint64(QTcpSocket::*)(const QByteArray &) >( &QTcpSocket::write )
:QTcpSocket中,叫write的有很多个,所以要依靠
qint64(QTcpSocket::*)(const QByteArray &)
去指定出来是哪一个。这是C++的方法,和Qt无关。

&socket
:表示要执行谁的write,有点类似于指针的角色

QByteArray( "xxxx" )
:调用write时给的参数

除了IO相关的类,其他有一些Qt的类也不可以跨线程操作,比如说QTimer,也会报错

QObject::startTimer: Timers cannot be started from another thread


按照上面说的调用原理,可以这样写:

QMetaObject::invokeMethod( &timer, std::bind( static_cast< void(QTimer::*)(int) >( &QTimer::start ), &timer, 1000 ) );


对了,start是一个槽函数,所以如果借助moc系统的话,可以这样写(两个写法是等价的)

QMetaObject::invokeMethod( &timer, "start", Q_ARG( int, 1000 ) );


注意!在QMetaObject::invokeMethod配合std::bind使用的时候,5.10.0版本的Qt会有内存泄漏,bug如下:

https://bugreports.qt.io/browse/QTBUG-65462


请注意你的Qt版本,以及bug的修复情况,酌情使用这个方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐