久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

【深入QT】信號槽機制淺析

 guitarhua 2016-10-11

一,、信號槽的基本概念

關(guān)于QT信號槽的基本概念大家都懂,,通過信號槽機制,QT使對象間的通信變得非常簡單:

A對象聲明信號(signal),,B對象實現(xiàn)與之參數(shù)相匹配的槽(slot),,通過調(diào)用connect進行連接,合適的時機A對象使用emit把信號帶上參數(shù)發(fā)射出去,,B對象的槽會就接收到響應(yīng),。

 

信號槽機制有一些特點:

1.   類型安全:只有參數(shù)匹配的信號與槽才可以連接成功(信號的參數(shù)可以更多,,槽會忽略多余的參數(shù)),。

2.   線程安全:通過借助QT自已的事件機制,,信號槽支持跨線程并且可以保證線程安全。

3.   松耦合:信號不關(guān)心有哪些或者多少個對象與之連接,;槽不關(guān)心自己連接了哪些對象的哪些信號。這些都不會影響何時發(fā)出信號或者信號如何處理。

4.   信號與槽是多對多的關(guān)系:一個信號可以連接多個槽,一個槽也可以用來接收多個信號,。

 

使用這套機制,類需要繼承QObject并在類中聲明Q_OBJECT,。下面就對信號槽的實現(xiàn)做一些剖析,了解了這些在使用的時候就不會踩坑嘍,。

二,、信號與槽的定義

槽:用來接收信號,,可以被看作是普通成員函數(shù),可以被直接調(diào)用,。支持public,,protected,,private修飾,,用來定義可以調(diào)用連接到此槽的范圍。

1. public slots:  

2.     void testslot(const QString& strSeqId);  

信號:只需要聲明信號名與參數(shù)列表即可,,就像是一個只有聲明沒有實現(xiàn)的成員函數(shù),。

1. signals:  

2.     void testsignal(const QString&); 

QT會在moc的cpp文件中實現(xiàn)它(參考下面代碼)。下面代碼中調(diào)用activate的第三個參數(shù)是類中信號的序列號,。

1. // SIGNAL 0  

2. void CTestObject:: testsignal (const QString & _t1)  

3. {  

4.     void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };  

5.     QMetaObject::activate(this, &staticMetaObject, 0, _a);  

6. }  

三,、信號槽的連接與觸發(fā)

通過調(diào)用connect()函數(shù)建立連接,會把連接信息保存在sender對象中,;調(diào)用desconnect()函數(shù)來取消。

connect函數(shù)的最后一個參數(shù)來用指定連接類型(因為有默認,,我們一般不填寫),,后面會再提到它。

1. static bool connect(const QObject *sender, const QMetaMethod &signal,  

2.     const QObject *receiver, const QMetaMethod &method,  

3.     Qt::ConnectionType type = Qt::AutoConnection);  

一切就緒,,發(fā)射,!在sender對象中調(diào)用:

1. emit testsignal(“test”);  

1. # define emit  

上面代碼可以看到emit被定義為空,這樣在發(fā)射信號時就相當于直接調(diào)用QT為我們moc出來的函數(shù)testsignal(constQString & _t1),。

具體的操作由QMetaObject::activate()來處理:遍歷所有receiver并觸發(fā)它們的slots,。針對不同的連接類型,,這里的派發(fā)邏輯會有不同。

四,、不同的連接類型剖析

QueuedConnection:向receiver所在線程的消息循環(huán)發(fā)送事件,,此事件得到處理時會調(diào)用slot,像Win32的::PostMessage,。

BlockingQueuedConnection:處理方式和QueuedConnection相同,,但發(fā)送信號的線程會等待信號處理結(jié)束再繼續(xù),像Win32的::SendMessage,。

DirectConnection:在當前線程直接調(diào)用receiver的slot,,這種類型無法支持跨線程的通信。

AutoConnection:當前線程與receiver線程相同時,,直接調(diào)用slot,,否則同QueuedConnection類型。

 

1. QObject * const receiver = c->receiver;  

2. const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;  

3.   

4. // determine if this connection should be sent immediately or  

5. // put into the event queue  

6. if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)  

7.     || (c->connectionType == Qt::QueuedConnection)) {  

8.         queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);  

9.         continue;  

10. #ifndef QT_NO_THREAD  

11. else if (c->connectionType == Qt::BlockingQueuedConnection) {  

12.     locker.unlock();  

13.     if (receiverInSameThread) {  

14.         qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "  

15.             "Sender is %s(%p), receiver is %s(%p)",  

16.             sender->metaObject()->className(), sender,  

17.             receiver->metaObject()->className(), receiver);  

18.     }  

19.     QSemaphore semaphore;  

20.     QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,  

21.         c->callFunction,  

22.         sender, signal_absolute_index,  

23.         0, 0,  

24.         argv ? argv : empty_argv,  

25.         &semaphore));  

26.     semaphore.acquire();  

27.     locker.relock();  

28.     continue;  

29. #endif  

30. }  

31.  

32. // 接下來的代碼會直接在當前線程調(diào)用receiverslot函數(shù)

五,、QT對象所屬線程的概念

這里要引入QObject的所屬線程概念,,看一下QObject的構(gòu)造函數(shù)(隨便選擇一個重載)就一目了然了。

如果指定父對象并且父對象的當前線程數(shù)據(jù)有效,,則繼承,,否則把創(chuàng)建QObject的線程作為所屬線程。

1. QObject::QObject(QObject *parent)  

2.     : d_ptr(new QObjectPrivate)  

3. {  

4.     Q_D(QObject);  

5.     d_ptr->q_ptr = this;  

6.     d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();  

7.     d->threadData->ref();  

8.     if (parent) {  

9.         QT_TRY {  

10.             if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))  

11.                 parent = 0;  

12.             setParent(parent);  

13.         } QT_CATCH(...) {  

14.             d->threadData->deref();  

15.             QT_RETHROW;  

16.         }  

17.     }  

18.     qt_addObject(this);  

19. }  

 

通過activate()的代碼可以看到,,除了信號觸發(fā)線程與接收者線程相同的情況能直接調(diào)用到slot,,其它情況都依賴事件機制,也就是說receiver線程必須要有QT的eventloop,,否則slot函數(shù)是沒有機會觸發(fā)的,!

當我們奇怪為什么信號發(fā)出slot卻不被觸發(fā)時,可以檢查一下是否涉及到跨線程,,接收者的線程是否存在激活的eventloop,。

所幸,我們可以通過調(diào)用QObject的方法movetothread,,來更換對象的所屬線程,,將有需求接收信號的對象轉(zhuǎn)移到擁有消息循環(huán)的線程中去以確保slot能正常工作。

 

有一個和對象所屬線程相關(guān)的坑:QObject::deletelater() ,。從源碼可以看出,,這個調(diào)用也只是發(fā)送了一個事件,等對象所屬線程的消息循環(huán)獲取控制權(quán)來處理這個事件時做真正的delete操作,。

所以調(diào)用這個方法要謹慎,,確保對象所屬線程具有激活的eventloop,不然這個對象就被泄露了,!

 

1. void QObject::deleteLater()  

2. {  

3.     QCoreApplication::postEvent(thisnew QEvent(QEvent::DeferredDelete));  

4. }  

六,、強制線程切換

當對象中的一些接口需要確保在具有消息循環(huán)的線程中才能正確工作時,,可以在接口處進行線程切換,這樣無論調(diào)用者在什么線程都不會影響對象內(nèi)部的操作,。

下面的類就是利用信號槽機制來實現(xiàn)線程切換與同步,,所有對testMethod()的調(diào)用都會保證執(zhí)行在具有事件循環(huán)的線程中。

1. class CTestObject : public QObject  

2. {  

3.     Q_OBJECT  

4.   

5. public:  

6.     CTestObject(QObject *parent = NULL)  

7.         : QObject(parent)  

8.     {  

9.         // 把自己轉(zhuǎn)移到帶有事件循環(huán)的QThread  

10.         this->moveToThread(&m_workThread);  

11.   

12.         // 外部調(diào)用一律通過信號槽轉(zhuǎn)移到對象內(nèi)部的工作線程  

13.         // 連接類型選擇為Qt::BlockingQueuedConnection來達到同步調(diào)用的效果  

14.         connect(this, SIGNAL(signalTestMethod(const QString &)), this, SLOT(slotTestMethod(const QString &)), Qt::BlockingQueuedConnection);   

15.           

16.         m_workThread.start();  

17.     }  

18.     ~CTestObject();  

19.   

20.     void testMethod(const QString& strArg)  

21.     {  

22.         if (QThread::currentThreadId() == this->d_func()->threadData->threadId)  

23.         {  

24.             // 如果調(diào)用已經(jīng)來自對象所屬線程,,直接處理  

25.             slotTestMethod(strArg);  

26.         }   

27.         else  

28.         {  

29.             // 通過發(fā)送信號,,實現(xiàn)切換線程處理  

30.             emit signalTestMethod(strArg);  

31.         }  

32.     }  

33.   

34. signals:  

35.     void signalTestMethod(const QString&);  

36.   

37. private slots:  

38.     void slotTestMethod(const QString& strArg)  

39.     {  

40.         // 方法的具體實現(xiàn)  

41.     }  

42.   

43. private:  

44.     QThread                     m_workThread;  

45. };  

七、參考資料

源碼參考:Qt\4.8.4\src\corelib\kernel\qobject.cpp

InsideQt Series》系列文章:http://www./qt

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導購買等信息,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多