Qt有很多IO相关的类,比如说QTcpSocket、QFile,总的来说,在Qt的框架内使用,还是非常方便的。
但是用过其他框架IO类的人,可能有一个很不习惯,就是Qt的所有IO类,都不推荐或者不可以跨线程操作,不然就会报错,比如说操作QTcpSocket跨线程调用write接口,就会报错:
socket notifiers cannot be enabled from another thread
- 1
要解决这个问题,直观的说就是不要跨线程操作,网上也有很多类似的说明。
这也是有道理的,很多时候真的是设计问题导致的,因为设计失误出现了不应该有的跨线程操作。
当然也可以用信号和槽封装一下,但是这样会涉及很多不必要的代码,我个人觉得也太过于麻烦。
那么我这里就提供一个更简单的方法,对QTcpSocket跨线程调用代码如下:
QMetaObject::invokeMethod( &socket, std::bind( static_cast< qint64(QTcpSocket::*)(const QByteArray &) >( &QTcpSocket::write ), &socket, QByteArray( "xxxx" ) ) );
- 1
来分析一下这个 invokeMethod 调用,接口的定义是这样的
bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr)
- 1
context
:表示被调用的函数要在 哪个对象 的生存线程运行function
:被调用的函数
主要看这两个,后面都有缺省值,不用管。
在本例中context指定socket,就表示在socket的生存线程运行,这可能是任何线程,取决于你在哪里实例化这个socket。如果填写qApp,就表示指定在主线程运行。function
被赋值了一个std:bind
,这是因为write不是槽函数,使用起来还是有点麻烦,不能直接写名字走moc系统。所以要手动用std::bind
把函数给包起来。
关于这个std::bind的3部分:
std::bind( static_cast< qint64(QTcpSocket::*)(const QByteArray &) >( &QTcpSocket::write ), &socket, QByteArray( "xxxx" ) )
- 1
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
- 1
按照上面说的调用原理,可以这样写:
QMetaObject::invokeMethod( &timer, std::bind( static_cast< void(QTimer::*)(int) >( &QTimer::start ), &timer, 1000 ) );
- 1
对了,start是一个槽函数,所以如果借助moc系统的话,可以这样写(两个写法是等价的)
QMetaObject::invokeMethod( &timer, "start", Q_ARG( int, 1000 ) );
- 1
注意!在QMetaObject::invokeMethod
配合std::bind
使用的时候,5.10.0版本的Qt会有内存泄漏,bug如下:
https://bugreports.qt.io/browse/QTBUG-65462
- 1
请注意你的Qt版本,以及bug的修复情况,酌情使用这个方法。
转载自:https://blog.csdn.net/wsj18808050/article/details/79021588