QT獲取線程號函數currentThreadId()返回Qt::HANDLE 如何得到QString?
線程中調用:currentThreadId()返回的不是int也不是QString而是HANDLE,qDebug可以輸出,不知道如何轉成QString,不知道QT作者這樣設計的目的是什麼。
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().
我人工翻譯下:
那麼,這個函數的設計目的是什麼?我們知道,任何真·線程(即不是自己寫調度演算法模擬的偽線程)類,其實都是對操作系統的線程API的封裝。而Qt則是使用了QThread類對操作系統的線程進行封裝。那麼,用戶該如何直接和系統線程API交互,或者和其他使用系統API的多線程類庫交互?[static] Qt::HANDLE QThread::currentThreadId()
返回當前執行線程的句柄。警告:此函數返回的句柄是作為(Qt)內部使用的,不應出現在任何應用代碼中。警告:在Window中,返回值為當前線程的pseudo-handle對象。它無法用於數值對比。也就是說,此函數的返回值為Win32函數getCurrentThreadId()返回的Windows-Thread ID(DWORD類型),而並非Win32函數getCurrentThread()返回的Windows-Thread HANDLE(HANDLE類型)
於是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() const
Returns 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_WIN
threadText = QString::number(DWORD(QThread::currentThreadId()));
#elif Q_OS_LINUX
threadText = ...
#else
....
#endif
為何不直接簡單的一句搞定呢
QString threadText = QStringLiteral("@0x%1").arg(quintptr(QThread::currentThreadId()), 16, 16, QLatin1Char("0"));
這樣,不但編碼方便了,而且在不同平台下的描述方式也統一的,更適合題主需要的用QString描述thread的需求。
這裡我用了一些複雜特性,簡單點的話直接QString::number(quintptr(QThread::currentThreadId()))就行了,我這麼寫是為了提高可讀性和性能:- 使用QStringLiteral宏在編譯期構造好"@0x%1"對應的QString對象,多多少少提高一點點效率,比如這個QString會和__FILE__、__LINE__、__FUNCTION__一起記入每條日誌的話,能快一點是一點
- 使用QString::arg()進行格式化輸出,以對其輸出,方便閱讀。輸出格式是16進位,輸出寬度是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
自己發的問題還得自己來回答,悲哀。
話說currentThreadId 這個B返回的是HANDLE,而不是QString。HANDLE 是一種 VOID* 類型,就是無類型指針,類似js中的var;
void* 類型具體介紹:void與void*詳解
直接強制轉換 int即可。例如:(int)currentThreadId();把他的指針轉換成int,得到int指針的數值(內存地址)。OK。推薦閱讀:
※qt窗口之間如何通信或信息共享?
※想學QT,是直接QT5還是先學QT4好?
※如何選擇C++與QML交互的兩種方式?
※QT溫度計折線圖如何實現?
※如何閱讀Telegram(Desktop)的源代碼?
TAG:QtC開發框架 |