Discuz! Board

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1861|回复: 1
打印 上一主题 下一主题

QT获取线程号函数currentThreadId()返回Qt::HANDLE 如何得到QString?

[复制链接]

388

主题

602

帖子

2218

积分

金牌会员

Rank: 6Rank: 6

积分
2218
跳转到指定楼层
楼主
发表于 2016-10-29 16:55:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
https://www.zhihu.com/question/47384794
作者:诸葛不亮
链接:https://www.zhihu.com/question/47384794/answer/117820704
来源:知乎
著作权归作者所有,转载请联系作者获得授权。

Qt::HANDLE 不是让你用来输出的

让我们看下Qt Document原文吧
[static] Qt::HANDLE QThread::currentThreadId()
Returns the thread handle of the currently executing thread.
Warning: The handle returned by this function is used for internal purposes and should not be used in any application code.
Warning: On Windows, the returned value is a pseudo-handle for the current thread. It can't be used for numerical comparison. i.e., this function returns the DWORD (Windows-Thread ID) returned by the Win32 function getCurrentThreadId(), not the HANDLE (Windows-Thread HANDLE) returned by the Win32 function getCurrentThread().
我人工翻译下:
[static] Qt::HANDLE QThread::currentThreadId()
返回当前执行线程的句柄。
警告:此函数返回的句柄是作为(Qt)内部使用的,不应出现在任何应用代码中。
警告:在Window中,返回值为当前线程的pseudo-handle对象。它无法用于数值对比。也就是说,此函数的返回值为Win32函数getCurrentThreadId()返回的Windows-Thread ID(DWORD类型),而并非Win32函数getCurrentThread()返回的Windows-Thread HANDLE(HANDLE类型)

那么,这个函数的设计目的是什么?
我们知道,任何真·线程(即不是自己写调度算法模拟的伪线程)类,其实都是对操作系统的线程API的封装。而Qt则是使用了QThread类对操作系统的线程进行封装。
那么,用户该如何直接和系统线程API交互,或者和其他使用系统API的多线程类库交互?
于是Qt提供了currentThreadId()函数,可以将QThread内部封装的线程标识取出来,这个标识不可跨平台,在不同平台下有不同的表示方式,所以Qt用Qt::HANDLE类型,即void*来包装。

而这个标识的作用是什么?正如文档所说,它返回的并不是线程的句柄,也就是操作线程的系统API对象,而是线程的id号,也就是操作系统对于这个线程给予的一个标识,一个key
那么如果我要通过系统API对此线程进行操作,这个key就用得上了。就像Linux下的socket()函数,返回一个int类型的socket id,以后所有对这个socket的操作都是用这个值作为标识一样。

所以可以看到了吧,这个东西的设计目的就不是为了用来输出。qDebug()可以输出,但输出的结果并不正确,因为它其实是个void*,qDebug()只能简单的把这个指针指向的内存地址输出出来,并非输出它的实际含义

如果非要转为人类可识别的方式进行输出,如QString,那么题主就需要查询相关平台的API手册,比如在windows上,就需要把它转换为DWORD类型,然后再作为无符号整形数据进行输出,这才是实际的内容。

然而这种方式是违背Qt初衷的——Qt设计目的是为了跨平台。虽然Qt许多针对平台的封装里都提供了类似的接口,可以让用户去除封装,和系统API对接,但这个在各平台都能用的“跨平台接口”其实本质上破坏了跨平台特性。
这里,我们可以看下这个方法的文档:
WId QWidget::winId() constReturns the window system identifier of the widget.
Portable in principle, but if you use it you are probably about to do something non-portable. Be careful.
If a widget is non-native (alien) and winId() is invoked on it, that widget will be provided a native handle.
On OS X, the type returned depends on which framework Qt was linked against. If Qt is using Carbon, the {WId} is actually an HIViewRef. If Qt is using Cocoa, {WId} is a pointer to an NSView.
This value may change at run-time. An event with type QEvent::WinIdChange will be sent to the widget following a change in window system identifier.
请注意我加粗的这一行:
此接口为了可移植性,但如果你使用了它,这意味着你可能准备做一些无法移植的操作。当心。
正如使用currentThreadId()获取线程id,来进行显示输出一样,在不同平台,我们就得编写不同的代码分支来处理这个Qt::HANDLE,破坏了移植性的同时,也破坏了代码的美观程度,提高了维护成本

这时,何不直接qDebug() << QThread::currentThread()呢?同样是返回线程标识,至少这个函数返回的是统一的QThread*指针。
至于含义么——QThread*和Qt::HANDLE都只是作为一个对象标识,具体的数据并没有实际意义,我们只需要用其能区分开不同的对象即可。

所以,相比于
QString threadText;#ifdef Q_OS_WINthreadText = QString::number(DWORD(QThread::currentThreadId()));#elif Q_OS_LINUXthreadText = ...#else....#endif
为何不直接简单的一句搞定呢
QString threadText = QStringLiteral("@0x%1").arg(quintptr(QThread::currentThreadId()), 16, QLatin1Char('0'));
这样,不但编码方便了,而且在不同平台下的描述方式也统一的,更适合题主需要的用QString描述thread的需求。

这里我用了一些复杂特性,简单点的话直接QString::number(quintptr(QThread::currentThreadId()))就行了,我这么写是为了提高可读性和性能:
  • 使用QStringLiteral宏在编译期构造好"@0x%1"对应的QString对象,多多少少提高一点点效率,比如这个QString会和__FILE__、__LINE__、__FUNCTION__一起记入每条日志的话,能快一点是一点
  • 使用QString::arg()进行格式化输出,以对其输出,方便阅读。输出宽度是16字符,为的是兼容32位和64位平台——64位平台下,指针是8字节而非4字节
  • 把指针转为quintptr类型,这个是qt自制的union类型,可以当指针用,也可以当整形用,方便用在打印地址的情况,适合替代void*
  • 输出格式为@0x1234567890abcdef,和Qt调试模式下的内存地址描述格式统一,方便阅读
  • 相对于threadId记录了平台实际的thread编号来说,QThread*也记录了thread对象地址,在需要进行debug时同样可以用来追踪现场

参考:
http://doc.qt.io/qt-5/qthread.html#currentThreadId

回复

使用道具 举报

388

主题

602

帖子

2218

积分

金牌会员

Rank: 6Rank: 6

积分
2218
沙发
 楼主| 发表于 2016-10-29 17:10:05 | 只看该作者
HANDLE 是一种 VOID* 类型,就是无类型指针,类似js中的var;

void* 类型具体介绍:void与void*详解

直接强制转换 int即可。
例如:(int)currentThreadId();
把他的指针转换成int,得到int指针的数值(内存地址)。

作者:约翰波特
链接:https://www.zhihu.com/question/47384794/answer/105729490
来源:知乎
著作权归作者所有,转载请联系作者获得授权。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|firemail ( 粤ICP备15085507号-1 )

GMT+8, 2024-5-16 16:38 , Processed in 0.061700 second(s), 18 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表