Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1695553
  • 博文数量: 584
  • 博客积分: 13857
  • 博客等级: 上将
  • 技术积分: 11883
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-16 09:34

分类: C/C++

2011-01-22 14:55:30

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()

阅读(1671) | 评论(0) | 转发(2) |
0

上一篇:QT多线程

下一篇:Qt事件

给主人留下些什么吧!~~