1. 程式人生 > >Qt:解決跨執行緒呼叫socket/IO類,導致報錯的問題(socket notifiers cannot be enabled from another thread)

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

Qt有很多IO相關的類,比如說QTcpSocket、QFile,總的來說,在Qt的框架內使用,還是非常方便的。 但是用過其他框架IO類的人,可能有一個很不習慣,就是Qt的所有IO類,都不推薦或者不可以跨執行緒操作,不然就會報錯,比如說操作QTcpSocket跨

一個疑惑的問題QObject::killTimer: Timers cannot be stopped from another thread

QObject的connect函式有幾種連線方式, a) DirectConnection,訊號傳送後槽函式立即執行,由sender的所線上程執行; b) QueuedConnection,訊號傳送後返回,相關槽函式由receiver所在的執行緒在返回到事件迴圈後執行;

執行呼叫Windows窗體控制元件

當我們需要處理大量資料時,為了使窗體介面不出現假死狀態,需要使用多執行緒進行處理。 當利用執行緒池ThreadPool.QueueUserWorkItem(t=>{ });進行多執行緒處理時,如果{ }中有控制元件(textbox,combox.....),程式就會報錯:不允許跨執行緒呼叫

C++網路程式設計實戰專案--Sinetlib網路庫3——事件迴圈與執行呼叫

上一篇文章講了Reactor模式的關鍵結構I/O複用和事件分發,現在我們來關注一下它們的使用。 事件迴圈 我們已經實現了一個Epoller類來實現I/O複用,具體的使用方法就是Epoller::Poll()函式等待事件的發生,該函式有一個超時時間,超過這個時間即

執行呼叫窗體控制元件

1、建立方法 Action<string> 方法 = (引數) => { This.控制元件.Text = x.ToString(); }; 2、呼叫方法 This.控制元件.Invoke(方法, 引數);

執行呼叫DataGridView控制元件

訪問 Windows 窗體控制元件本質上不是執行緒安全的。如果有兩個或多個執行緒操作某一控制元件的狀態,則可能會迫使該控制元件進入一種不一致的狀態。還可能出現其他與執行緒相關的 bug,包括爭用情況和死鎖。確保以執行緒安全方式訪問控制元件非常重要。  雖然可以使用如下:

C# 執行呼叫TextBox方法淺析

 首先來看下面程式碼:   主執行緒:  delegate void SetTextCallback(string text);   private void SetText(string text)   {   if (this.textBox1.InvokeRequir

c#中如何執行呼叫windows窗體控制元件?

我們在做winform應用的時候,大部分情況下都會碰到使用多執行緒控制介面上控制元件資訊的問題。然而我們並不能用傳統方法來做這個問題,下面我將詳細的介紹。 首先來看傳統方法: public partial class Form1 : Form { public Form1(

C# 執行呼叫窗體控制元件

//定義委託 delegate void MyInvoke(string s); //呼叫方法 private void SetText(string s) {

方法快解決執行的同步安全問題以及其中鎖的問題

package cn.itcast.demo1; /* 採用同步方法形式,解決執行緒的安全問題 好處: 程式碼簡潔 將執行緒共享資料,和同步,抽取到一個方法中 在方法的宣告上,加入同步關鍵字 問題: 同步方法有鎖嗎,肯定有,同步方法中的物件鎖

Linux使用多執行程式設計和訊息佇列實現兩個程序之間的聊天

思路: 一個檔案:建立一個執行緒和主函式,或者建立兩個執行緒主函式呼叫(我用這種)。 建立兩個訊息佇列, 一共兩個檔案,兩個佇列,四個程序 a.c    一個程序寫(訊息型別為1)   ---->>佇列     一個程序讀(訊息型別為2) b.c   一

執行呼叫單例中的方法會不會造成執行安全問題

區域性變數不會受多執行緒影響 成員變數會受到多執行緒影響 多個執行緒應該是呼叫的同一個物件的同一個方法: 如果方法裡無成員變數,那麼不受任何影響 如果方法裡有成員變數,只有讀操作,不受影響                      存在寫操作,考慮多執行緒影響值 當多個執行

執行安全的集合、CopyOnWrite機制介紹

看過併發程式設計的書,這兩種機制都有所瞭解,但不紮實其實。看到別人的部落格描述的很精闢,於是轉過來,感謝! 原文連結:https://blog.csdn.net/yen_csdn/article/details/51705687   inkedList、ArrayList、HashSet是非

下列關於執行排程的敘述中錯誤的是。----阿里巴巴2015校招研發線上

下列關於執行緒排程的敘述中,錯誤的是()。 正確答案: B E   你的答案: A E F (錯誤) 呼叫執行緒的sleep()方法,可以使比當前執行緒優先順序低的執行緒獲得執行機會 呼叫執行緒的yeild()方法,只會使與當前執行緒相同優先順序的執行緒

(轉)啟動網卡Failed to start LSB: Bring up/down networking 解決辦法總結

deb contain ade nag disabled dev nta container save 啟動網卡報錯(Failed to start LSB: Bring up/down networking )解決辦法總結 原文:http://blog.51cto.com

windows下scrapy安裝問題以及Twisted安裝error: Microsoft Visual C++ 14.0 is required.完美解決辦法

方法1(通常是失敗的) 1. 命令列執行: pip3 install scrapy 不管是網路問題也好,缺少相關的包也好,用這條命令安裝scrapy我就沒成功過。。。難受 方法2(成功) 手動安裝相關的包。 1. lxml安裝命令(沒問題): pip3 install lxml

【轉】【centos】啟動網絡卡Failed to start LSB: Bring up/down networking 解決辦法總結

今天一臺一直在用的虛擬機器重啟後,CRT連線不上,ip也ping不通,重啟網絡卡報錯,“Failed to start LSB: Bring up/down networking”,參考:http://blog.51cto.com/11863547/1905929,解決。 遇到這個錯誤好幾次,所以總結了一下

解決idea新引進的專案總是maven的dependencies有圖

如下圖這種紅線:  解決方法: 1.首先點選這個設定。 2.確定電腦的maven路徑  3.點選ok之後, 點選pom.xml檔案開啟 4.將有錯的地方ctrl+f  5.將圈起來的地方ctrl+x,等待右邊目錄重新整理,這條目錄消失,然後ct

tomcat老是執行之前被我刪除的web專案導致

!,首先刪除tomca對專案的快取  你所裝的tomcat安裝目錄-》me-webapps->你專案的資料夾 (同樣的webapps,work)一樣的操作 1.該專案的War檔案從***\apache-tomcat\webapps 移出; 2.該專案的某些配置

QT 執行傳送訊號非元資料訊號解決

參考部落格: http://qimo601.iteye.com/blog/1673578 http://blog.csdn.net/luotuo44/article/details/39395025 http://blog.csdn.net/seanyxie/articl