读书摘要--《C++.GUI.Programming.with.Qt.4》chapter18~20

Chapter 18 Multithreading



18.1        Creaing Threads

      Qt中提供多线程的机制很简单:创建QThread的派生类,并重新实现其保护成员函数run()。
     
      QThread::run(),被调用来开始线程的执行,在run()结束时线程终止。
     
      QThread::terminate(),用来终止线程的执行,非阻塞操作,并不保证线程的立即终止;可以在调用QThread::terminate()之后调用QThread::wait()来实现同步等待。
     
      terminate()并不是值得推荐结束线程的方法,因为它强制线程终止而不给线程任何清场的机会。
     
18.2        Synchronizing Threads

      Qt提供的用于线程同步的类包括QMutexQReadWriteLockQSemaphoreQWaitCondition
     
QMutex

      QMutex::lock()                    阻塞操作
      QMutex::trylock()              非阻塞操作
      QMutex::unlock()     
     
      QMutexLocker是Qt提供的用于简化Mutex操作的一个类--QMutexLocker的构造函数以一个QMutex对象为参数,并对其自动执行lock操作;而在析构函数则对其自动执行unlock操作。
     
QReadWriteLock可以允许同时进行多个读操作或一个写操作。
     
      QReadWriteLock::lockForRead()
      QReadWriteLock::lockForWrite()
      QReadWriteLock::unlock()
     
QSemaphore是对Mutex的扩展;与读写锁不同的是,信号量可以用来保护一批相同的资源,而不只是一个。
     
      QSemaphore::acquire(int n=1)
      QSemaphore::release(int n=1)
      QSemaphore::available()
     
QWaitCondition和QMutex联合使用,可以允许一个线程在某个条件满足时唤醒其他线程,比起单独使用QMutex能实现更精确的控制
     
      QWaitCondition::wait()的参数是一个状态为locked的QMutex,该函数在阻塞本线程前会将这个QMutex解锁,并在函数返回前对其lock。
     

TLS(thread-local storage)

     
较好的实现方法是使用QThreadStorage< T> 类,该类常用来实现cache,这样可以避免使用mutex时lock,unlock以及等待带来的开销。
     
      由于某些编译器的问题,QThreadStorage< T> 中只能存放指针

      QThreadStorage::hasLocalData()
      QThreadStorage::setLocalData()

     
18.3        Communicating with MainThread
     
      当Qt程序运行时,主线程是唯一的线程,并且是唯一允许创建QApplication或QCoreApplication对象并对其调用exec()的线程。在调用exec()之后,主线程要么是在等待event的发生,要么是在处理一个event。
     
      主线程可以通过创建QThread的子类来开始新线程
     
      之前介绍的mutex,read/write lock,semaphore等均可用于新线程之间的通讯,但是却不能用于和主线程的通讯,因为这会导致主循环的event loop被阻塞并" 冻结" UI。
     
      解决方案是在主线程与新线程之间跨线程的使用signal-slot机制
     
      通常情况下signal-slot机制是同步工作的,这意味着当signal被emit时,与之想联系的slot会被立即调用。
     
      然而,当该机制用于将不同线程中的object连接起来时,则变为异步机制。这样的连接是在底层是通过创建并传递event来实现的;slot被signal的接收对象所在的线程的event loop所调用。
     
      默认情况下,一个QObject对象存在于其被创建的线程之中;这可以在任何时候调用QObject::moveToThread()被改变。
     

18.4        Using Qt's Classess in Secondary Threads

      Thread-safe & Reentrant        注意留意这两个概念应用在函数和类之上的不同。
     
      对于类,如果它的所有成员函数都可以被不同线程同时调用而不相互影响--即使这些调用是针对同一个类对象,那么该类被定义为thread-safe。

      对于类,如果其不同实例可以在不同线程中被同时使用而不相互影响,那么该类被定义为reentrant;然而,不同线程中同时访问同一个reentrant类对象,并不是安全的,这样的访问需要用mutex进行保护。
     
      在Qt的定义中,在类这个层次,thread-safe是比reentrant更严格的要求,这和在函数层次上的关系正好相反。
     
      通常情况下C++的类只要不使用全局或其它共享变量,就是reentrant的。

      Qt中大多数non-GUI的类,是属于reentrant的。QObject是reentrant的,但是需要注意以下几点:
     
      1).        子QObject必须在父QObject所属的线程中被创建,这意味着在非主线程中的对象在创建时不允许以QThread作为parent,因为后者是在主线程或另外一个非主线程中被创建的。
      2).        在删除一个QThread对象前,必须将对应线程中创建的所有对象都销毁。
      3).        对象必须在其被创建的线程中被删除。

        如果需要删除存在于另一个线程中的对象,必须调用线程安全的QObject::deleteLater()函数,该函数会发送一个" defered delete" event。
       
        QWidget及其子类不是reentrant的
       
     


Chapter 19 Creating Plugins


      Qt提供了Qlibrary类,用于以一种平台无关的方式实现在程序运行时加载共享库。
     
19.1        Extending Qt with Plugins

      Qt本身可以被很多类型的plugin扩展,最常见的包括database drivers,image formats,text codecs等。
     
      对于每种类型的plugin,通常都需要两个类:一个wrapper类实现该类通用的plugin API,以及一个或多个handler类,每个类实现一个plugin特定的API。
     
      在plugin的.cpp文件中,需要使用Q_EXPORT_PLUGIN()这个宏来确保plugin能够被Qt识别。
     
      plugin真正执行的操作都是通过其handler类来实现的。
     
      plugin的.pro文件与应用程序不同。默认情况下.pro文件使用app模板,然而这里必须使用lib模板,因为plugin属于库,而不是一个独立的应用程序。
           
      QCoreApplication::addLibraryPath( ),为程序添加新的库路径。

19.2        Making Application Plugin-Aware


      应用程序的plugin实际是实现了一个或多个接口(interface)的动态库。应用程序与plugin之间的通讯是通过interface的virtual table来完成的。
     
      一个接口(interface)通常声明一个virtual析构函数,一个返回QStringList的virtual函数,以及一个或多个其他virtual函数。
     
      在接口声明的尾部,需要调用Q_DECLARE_INTREFACE2()来将该interface与某个标识符关联起来。
     
      QPluginLoader类用于在运行时加载plugin。

      QPluginLoader::load(),通常不需要显式调用,因为instance()函数会在必要时调用该函数完成加载。
      QPluginLoader::instance(),返回一个指向plugin对象的QObject *指针。
     
      同一个插件plugin可以成功cast至多个interface,因为plugin可以通过多重继承来提供多个interface。

19.3        Writing Application Plugins

      应用程序的plugin是其要提供的interface和QObject二者的子类。
     
      在plugin的源代码中,需要为其提供的每个Interface都要使用Q_INTERFACES()宏,来保证moc和qobject_cast< T> 之间的协调工作。
     
      在.cpp文件的尾部,同样需要调用Q_EXPORT_PLUGIN2()宏来使该plugin对于Qt可用。
     



Chapter 20 Platform-Specific Features



20.1        Interfacing with Native APIs

      在每个平台上,Qt都为QWidget提供了一个winId()函数,返回window ID或是句柄;QWidget还提供了一个静态函数find(),返回一个特定window ID对应的widget。我们可以将获得的window ID传递给Native API来执行平台特定的操作。

      Qt定义了以下系统标志:Q_WS_WINQ_WS_X11Q_WS_MACQ_WS_QWS(Qtopia)。
     
      QSysInfo::WindowsVersion  QSysInfo::MacintoshVersion      这两个静态变量存储着WIN和MAC操作系统的版本信息
     
20.2        Using ActiveX on Windows

      ActiveX构建于Microst COM之上,它为使用组件的应用程序定义了一套接口,为提供组件的库和应用程序定义了另一套接口。
     
      ActiveQt由两个模块组成:
     
              QAxContainer模块允许用户使用COM object并在Qt程序中内嵌ActiveX控件。

              QAxServer模块允许用户导出自定义的COM object以及用Qt编写的ActiveX控件。
     
      Q_ENUMS()宏的作用是告知moc其" 宏参数" 是枚举类型。
     

      QAxContainer模块由三个类组成:QAXObject封装一个COM object,QAxWidget封装一个ActiveX控件,QAxBase为QAxObject和QAxWidget实现核心COM功能。
     
      QAxObject派生自QAxBase和QObject,QAxWidget派生自QAxBase和QWidget。
     
      COM中的数据类型会被自动转换为合适的Qt数据类型。
           
      QAxBase::setControl()
     
      QObject::setProperty()可用于设置COM property和Qt property。
     
      要链接QAxContainer库的话,需要在.pro文件中添加下列一行:" CONFIG +=qaxcontainer"
     
      QAxBase::dynamicCall()
     
      注意,QAxObject和QAxWidget的子类无法定义新的property,signal和slot。
     

      QAxServer模块允许将一个标准Qt程序转换为一个ActiveX server。该server可以是共享库,也可以是独立的应用程序。共享库形式的server被称为in-process servers,而独立应用程序形式的server被称为out-of-process server。
     
      QAxBindable在widget与ActiveX client之间提供了一个接口。
     
      在Qt中处理多重继承中,如果基类中存在QObject的派生类,必须将这样的类放在首位。
     
      QAXFACTORY_DEFAULT()宏的作用是导出一个AxtiveX控件,可以用于仅导出一个控件的ActiveX server;当server要导出多个控件时,不能使用QAXFACTORY_DEFAULT()宏。
     
      QApplication能够识别命令行中的-activex参数,并使应用程序作为server而运行。
     
      Q_CLASSINFO()宏

     
20.3        Handling X11 Session Management

      为了使一个Qt/X11应用程序意识到session manager的存在,需要重新实现QApplication::saveState()函数,并在该函数中保存应用程序的状态信息。
     
      当用户启动shutdown操作时,程序员可以通过重新实现QApplication::commitData()来获取控制权,这允许应用程序保存未保存的数据,并且与用户交互--如果可能的话;这部分session management在X11和Windows上都被支持。
     
      QObject::setObjectName()
     
      void QApplicatoin::saveState(QSessionManager &)--该函数在session manager希望应用程序保存其状态时被调用,QSessionManager类型的参数允许应用程序与session manager进行通讯。
     
           
      discard command:是指session manager必须执行的用删除任何存储当前状态信息的命令。
      restart command:是指session manager必须执行的用以重新启动应用程序的命令。
     
      QSessionManager::setDiscardCommand(QStringList &)
      QSessionManager::setDiscardCommand(QStringList & )
     
      默认情况下,Qt提供的restart command的格式为: appname -session id_key
     
      QSessionManager::release()
      QSessionManager::cancel()
     
      QApplication:isSessionRestored()

你可能感兴趣的:(C++,session,manager,读书,qt,interface)