zoukankan      html  css  js  c++  java
  • [转]QT多线程异步调用

    QT多线程异步调用,类似MFC的PostThreadMessage

    blog.csdn.net/dongfangyu/article/details/5930129

    在MFC中,每个界面线程都会有一个消息队列,通过函数PostThreadMessage,线程之间可以互发消息,由于Post的方式是非阻塞的,因此系统可以表现出很好的性能。这种消息机制是多线程之间异步调用的极佳方式。

    笔者最近学习QT4.5.3的时候,想在QT中找到类似于相似的机制,但是暂时没有发现。于是想,没有就创造它。以下描述我如何使QT拥有MFC的消息机制。

    MFC的消息机制,其实就是通过每个消息ID对应函数地址来实现的。明白了这个,构造此机制就简单了。首先使用StlPort的hash_map建立消息ID与回调函数地址的映射表,之所以用hash_map,是由于其时间复杂度很低,而且不会随着表项的增多而变慢,但会增加内存使用量,在现在内存较大的年代,这个问题不大。

    其次,是通过QThread继续,得到一个基类线程,该线程假设命名MsgThd,在该线程中,配备有hash_map,同时再使用StlPort的vector建立一个消息队列。重载QThread的run函数,其中是一个循环,该循环中不停从vector中尝试得到消息,若得到消息ID,则尝试根据此消息ID,从hash_map得到回调函数地址,若回调函数地址有效,则执行该函数。否则,休眠一定时间(比如1ms),然后继续从vector中尝试得到消息,周而复始。此时,凡是从MsgThd继续的线程已经具有消息队列了。

    再次,如何使用该MsgThd呢?方法是,凡是需要消息队列的线程,需从MsgThd继续,定义消息ID,定义回调函数,然后把它们放入hash_map中。举例,若线程A,B都是从MsgThd继续,若线程A要给线程B发一个异步消息,那么只需要线程A往线程B的vector中放入一个消息即可,若你喜爱的话,这个动作的函数接口可以写成PostThreadMessage。当然,存放消息的vector,须用加锁解锁,因为它极有可能出现线程A与线程B同时访问的情况,在QT中,可以使用互斥量QMutex。

    以上所说,是实现QT多线程异步调用的第一种方式。

    以下所说,第二种方式。这是笔者学习QT几天后发现的J,因为随着对QT的熟知,接触到越来越多的内容之后,便发现QT本身有这样的机制。

    而这种机制存在于函数QApplication::postEvent中,此函数相当于MFC的PostThreadMessage。网上有大量关于QT的讯息,但我还没有找到对这个问题讲得较清楚的。我以下解释,不是最清楚的,但说完了,大家就会使用了。

    新建一个类,比如MsgThd,继续自QThread,重写run函数,里面就一个函数exec();据说,exec()是消息机制的触发函数。重写event函数,据说,所有事件(Event)都会经过该函数,类似于MFC的PreTranslateMessage。(两个“据说”,说明说法仅供参考,笔者也是刚接触几天,莫怪笔者误人子弟。等我把QT源代码看完之后,就没有“据说”了,呵呵。)

    假设线程A需要给线程B发消息,在线程A中有代码举例如下:

    QEvent* pEvent = new QEvent((QEvent::Type)1234);

    QApplication::postEvent( pThread_B, pEvent );

    其中1234,是自己定义的,只要大于QT的保留值1024即可。就像你在MFC自定义消息的时候,需要大于WM_USER(0x0400)一样。

    上面两句的意思是说,线程A产生了一个事件,它发给了线程B。

    因此线程B中的event函数可能要这样写:

    bool Thread_B::event(QEvent * pEvent )

    {

    // 截获住自定义的事件

    if ( pEvent->type() == (QEvent::Type)1234 )

    {

                std::cout<<"这句话替换成你需要调用的函数"<<std::endl;

              }

    return MsgThd::event(pEvent);

    }

    这就是第二种方式,实现QT多线程异常调用。

    可以自訂事件類型,最簡單的方式,是透過QEvent::Type指定事件類型的常數值,在建構QCustomEvent時作為建構引數並透過postEvent()傳送事件,例如:

    const QEvent::Type MyEvent = (QEvent::Type) 9393;
    ...
    QApplication::postEvent(object, new QCustomEvent(MyEvent));

    自訂事件必須定義事件號碼(Event number),自定義的事件號碼必須大於QEvent::Type的列舉值,通常1024以下的值是保留給Qt預先定義的事件類型來使用。

    object是事件的接受者,使用 postEvent()方法時,事件必須以new的方式建立,在事件處理完畢之後,會自動將事件delete,postEvent()會將事件放至事件佇列的尾端,然後立即返回。若要強迫Qt馬上處理先前postEvent()排到佇列的事件,則可以使用sendPostedEvents()。
    您可以使用sendEvent()方法,
    事件會立即送至接受者,sendEvent()方法的事件不會被delete,所以通常建立在堆疊(Stack)區,例如:

    CustomEvent event("Event Message");
    QApplication::sendEvent(object, &event);


    自訂的事件類型必須是QEvent的子類別,通常繼承QCustomEvent類別,建立自訂事件類別可以獲得更多的型態安全(Type safety)。
    要處理自訂事件,可以重新定義customEvent()方法,例如:

    void CustomWidget::customEvent(QCustomEvent *event) {
    CustomEvent *customEvent = static_cast<CustomEvent *>(event);
        ....
    }

    或是重新定義event()方法,將自訂事件分派給其它函式或直接在event()中處理,例如:

    bool CustomWidget::event(QEvent *event) {
        if (event->type() == MyCustomEventType) {
            CustomEvent *myEvent = static_cast<CustomEvent *>(event);
            // 對自訂事件的處理,或呼叫其它函式來處理事件
            return true;
        }
        return QWidget::event(event);
    }

  • 相关阅读:
    基于element-ui图片封装组件
    计算时间间隔具体每一天
    C语言学习笔记 —— 函数作为参数
    AtCoder Beginner Contest 049 题解
    AtCoder Beginner Contest 048 题解
    AtCoder Beginner Contest 047 题解
    AtCoder Beginner Contest 046 题解
    AtCoder Beginner Contest 045 题解
    AtCoder Beginner Contest 044 题解
    AtCoder Beginner Contest 043 题解
  • 原文地址:https://www.cnblogs.com/pulas/p/2777623.html
Copyright © 2011-2022 走看看