Qt:解决跨线程调用socket/IO类,导致报错的问题(socket notifiers cannot be enabled from another thread)
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的修复情况,酌情使用这个方法。
Qt:解决跨线程调用socket/IO类,导致报错的问题(socket notifiers cannot be enabled from another thread)相关推荐
- Auto.js学习笔记10:实例化自定义对象,在子线程使用JSON.stringify()方法导致报错(已解决)
申明本人使用的autojs是4.1.1版本 JSON.stringify()使用导致autojs软件直接奔溃退出. 报错核心局部代码 var sendInfoObj = { //对象areaCode ...
- winform解决跨线程调用windows窗口控件
private delegate void Start(int type);private void StartDele(int type) {if (this.InvokeRequired){Sta ...
- Qt笔记-QTcpSocket跨线程调用(官方推荐方法,非百度烂大街方法)
TCP服务端的经典案例中有个例子,就是当收到TCP客户端连接后,线程池直接开一个线程然后把这个socket指针传到线程里面,依靠新开的线程进程业务处理. 但在Qt里面使用这个方式后,会报一个QTcpS ...
- 关于PrintQueueCollection()类,跨线程调用错误“线程无法访问此对象,因为另一个线程拥有该对象”
使用System.Printing.PrintQueueCollection() 纸质打印机获取 _generalPrinters = new PrintQueueCollection(); pu ...
- c#中如何跨线程调用windows窗体控件?
我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来做这个问题,下面我将详细的介绍. 首先来看传统方法: public partial ...
- 多线程总结之旅(12):跨线程调用控件的几种方式
本来是写完线程池就结束多线程总结之旅系列的,但是想想平时在项目中用到线程仅仅不够的,为什么这么说呢?举个例子:我们有一个函数,它的功能就是加载数据,然后绑定到datagridview.现在我们开启一个 ...
- 理解Windows窗体和WPF中的跨线程调用
你曾开发过Windows窗体程序,可能会注意到有时事件处理程序将抛出InvalidOperationException异常,信息为" 跨线程调用非法:在非创建控件的线程上访问该控件" ...
- C#跨线程调用窗体控件的问题
前段时间遇到跨线程调用窗体控件的问题,其实一句话System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;就可以解决,但 ...
- C# 跨线程调用控件
在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应. 同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线程间操作无效 第一种办法:禁 ...
最新文章
- 自然语言项目之Python语种检测代码实现
- SpringBoot给属性赋值的几种方式以及JSR303校验
- Python图片文字识别——Windows下Tesseract-OCR的安装与使用
- Java+jquery+jsonp实现跨域
- 九度OJ - 题目1481:Is It A Tree?
- 《JAVA疯狂讲义》学习笔记第六天
- 【开源】一个有趣的文字冒险游戏
- 找了个阅读pdf文件语音朗读的软件
- 4000亿市值迈瑞医疗的虚火与真金
- owncloud 私有云搭建
- 猫和老鼠玩象棋,玩了M+N局,猫赢了M局 老鼠赢了N局 NM,而且在整个过程中,猫的得分从来没有超过过老鼠,问共有多少种可能的比赛得分过程
- Vue中使用echart实现中国地图统计图
- 计算机域名的解释,通俗易懂:域名与IP的关系讲解
- proteus仿真的过程中,经常会不小心把示波器关掉,导致无法查看波形。那么如何调出示波器呢?
- 第一学历和最高学历哪个更重要?
- GetContactInfoUtils(一个获取手机联系人名称,电话,头像的工具类)
- 删除流氓软件 Alibaba PC Safe Service
- 6月程序员平均工资出炉,这个水平我慕了!
- 浮动IP的绑定 、释放、移除
- Java 面试/笔试题神整理 [Java web and android]