1. 程式人生 > >QT 訊號與槽在不同執行緒不能連線的問題

QT 訊號與槽在不同執行緒不能連線的問題

QObject::connect: Cannot queue arguments of type 'ERROR_LEVEL' 
(Make sure 'ERROR_LEVEL' is registered using qRegisterMetaType().)

其中ERROR_LEVEL只是我定義的列舉型別即enum ERROR_LEVEL。然後在Qt的訊號-槽函式的引數中使用了這個列舉型,在傳送訊號時就出現了上述警告。上面警告的大概意思是訊號佇列中無法使用ERROR_LEVEL型別,要使用qRegisterMetaType()註冊該型別後方可使用。

      通常使用的connect,實際上最後一個引數使用的是Qt::AutoConnection型別:
bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection )

Qt支援6種連線方式,其中3中最主要:
Qt::DirectConnection(直連方式)

      當訊號發出後,相應的槽函式將立即被呼叫。emit語句後的程式碼將在所有槽函式執行完畢後被執行。(訊號與槽函式關係類似於函式呼叫,同步執行)
Qt::QueuedConnection(排隊方式)

      當訊號發出後,排隊到訊號佇列中,需等到接收物件所屬執行緒的事件迴圈取得控制權時才取得該訊號,呼叫相應的槽函式。emit語句後的程式碼將在發出訊號後立即被執行,無需等待槽函式執行完畢。(此時訊號被塞到訊號佇列裡了,訊號與槽函式關係類似於訊息通訊,非同步執行)
Qt::AutoConnection(自動方式)

      Qt的預設連線方式,如果訊號的發出和接收這個訊號的物件同屬一個執行緒,那個工作方式與直連方式相同;否則工作方式與排隊方式相同。

      我的專案中的確跨執行緒使用了ERROR_LEVEL為引數型別的訊號,因此使用的應當是排隊方式的訊號-槽機制,出現“佇列中無法使用ERROR_LEVEL型別”的警告資訊就可以理解了。放狗搜了一圈,有篇文章提供了個這樣的解決方案:

connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)), 
            this,SLOT(sendRes(QUuid,QByteArray,bool))); 
改為: 
connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)), 
            this,SLOT(sendRes(QUuid,QByteArray,bool)), Qt::DirectConnection);

這樣做的確能使警告資訊消失,因為Qt官方文件寫了:

With queued connections, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them in an event behind the scenes.

即使用排隊方式的訊號-槽機制,Qt的元物件系統(meta-object system)必須知道訊號傳遞的引數型別。這裡手動改為直連方式,Qt的元物件系統就不必知道引數型別了,於是警告資訊消失。但這樣做是不安全的,見Qt官方文件:

Be aware that using direct connections when the sender and receiver live in different threads is unsafe if an event loop is running in the receiver's thread, for the same reason that calling any function on an object living in another thread is unsafe.

      因此,咱還是老老實實地用qRegisterMetaType()註冊型別吧,見程式碼:

qRegisterMetaType<ERROR_LEVEL>("ERROR_LEVEL");