Linux环境高级编程_第1页
Linux环境高级编程_第2页
Linux环境高级编程_第3页
Linux环境高级编程_第4页
Linux环境高级编程_第5页
已阅读5页,还剩115页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux环境高级编程

李林

电子科技大学软件学院

第三讲线程的封装

李林

电子科技大学软件学院

・线程的基本概念

・线程的创建与终止II

・线程创建的封装111

.线程的同步

-线程创建的再封装

■Windows消息在Linux的重现

第二讲线程的封装

.线程的基本概念1111

・线程的创建与终止111

・线程创建的封装1,11

.线程的同步IIIII

-线程创建的再封装

■Windows消息在Linux的重现

—线程的概念_________

■进程的所有信息对该进程的所有线程都是

共享的,包括可执行的程序文本、程序的

I全局内存、堆内存、文件描述符等

■线程独有的:线程ID、寄存器值、栈、信

.号屏蔽字、errno值、线程私有数据

■典型的UNIX进程,可以看成是只有一个线

程的进程

5

—线程的概念_________

■同进程一样,每个线程也有一个线程ID

■进程ID在整个系统中是唯一的,但线程ID

不同,线程ID只在它所属的进程环境中有

।效一।।।।।।।।।

■线程ID的类型是pthread_t,在Linux中的定

义:

A在/usr/include/bits/pthreadtypes.h中

>typedefunsignedlongintpthread_t;

6

■pthread_self函数可以使调用线程获取自己

的线程后...........................

■函数原型IIIIIIII

#include<pthread.h>

pthread_tpthread_self();

■返回调用线程的线程ID

7

—线程的概念_________

■Linux中使用整型表示线程ID,而其他系统

则不一定

■FreeBSD5.2.1>MacOSX10.3用一个指

向pthread结构的指针来表示pthread_t类型。

■为了移植性,在比较两个线程ID是否相同

时,可以使用pthread_equal函数

8

pthreadequal^S

■该函数用于比较两个线程ID是否相同

■函数原型.....................11

#include<pthread.h>

intpthread_equal(pthread_ttid1,pthread_ttid2);

■若相等则返回非0值,否则返回0

9

・线程的基本概念

・线程的创建与终止11

・线程创建的封装111

.线程的同步

-线程创建的再封装

■Windows消息在Linux的重现

线程的创建与终止

■pthread_create函数

■pthread_exit函数

■pthread_join函数

■pthread_cancel函数

■pthread_detach函数

11

线程的创建与终止

■pthread_create函数

■pthread_exit函数

■pthread_join函数

■pthread_cancel函数

■pthread_detach函数

12

一线程的创建

■pthread_create函数用于创建一个线程

■函数原M..................................................

#include<pthread.h>

intpthread_create(pthread_t*restricttidp,

constpthread_attr_t*restrictattr,

void*(*start_rtn)(void*),

void*restrictarg);

■参数与返回值

>tidp:当pthread_create成功返回时,该函数将线程ID

存储在tidp指向的内存区域中

13

rest,jet关键字________

■restrict关键字C99标准引入的,

,.只能用于限定指针..................

B表明指针是访问一个数据对象的唯一且初始

的方式

resjcj;关键字________

intar[10];

int*restrictrestar=(int*)malloc(10*sizeof(int));

int*par=ar;

for(intn=0;n<10;n++){、

par[n]+=5;A

.restar[n]+=5;।'int*pnew=restar;

HB]i[[\_______________________?

ar[n]*=2;

par[n]+=3;

restar[n]+=3;

}

resjcj;关键字________

intar[10];

int*restrictrestar=(jnt*)malloc(10*sizeof(int));

int*par=ar;

for(intn=0;n<10;n++){/

par[n]+=5;~~/-------------\

『estar[n]+=5;::[int*吵(restar;

ar[n]*=2;/

par[n]+=3;

restar[n]+=3;

re&±rjet关键字

intar[10];

int*restrictrestar=(int*)malloc(1O*sizeof(int));

int*par=ar;

for(intn=0;n<10;n++){

par[n]+=5;

对restar的操作进行优化,

restar[n]+=8

restar[n]+=5;

无法对par的操作优化

Lr[n]*=2;]

par[n]+=8

I什么是编译单元?

par[n]+=3;

restar[n]+=3;

}

pthreadcrgate函数

■参数与返回值11111

0>attr:用于定制各种不同的线程属性,将在后

面部分讨论。通常可设为NULL,采用默认线

J呈扈性

>start_rtn:线程的入口函数,即新创建的线程

从该函数开始执行。该函数只有一个参数,即

arg,返回一个指针

>arg:作为start_rtn的第一个参数

A成功返回0,出错时返回各种错误码

18

线程的创建_________

■示例3.1

>编译:#g++test.cpp-Ipthread

»程序3.1没有任何输出。其原因在于主线程先

!!步新创建的线程退出।।।।।

*A什么是主线程?

■示例3.2

»如何能够等待线程的结束?pthreadjoin

19

线程的创建与终止

■pthread_create函数

■pthread_exit函数

■pthread_join函数

■pthread_cancel函数

■pthread_detach函数

20

一线程的终止

■单个线程的三种退出方式

A线程从启动例程中返回,返回值是线程的退出

II码IIIIIIIIII

।»线程被同一进程中的其他线程取消j।

>线程调用pthread_exit函数

21

pthreadexit函数

■该函数让线程退出1,1

#include<pthread.h>

voidpthread_exit(void*rval_ptr);

■参数

>rval_ptr:与线程的启动函数类似,该指针将

传递给pthread_join函数

22

线程的创建与终止

■pthread_create函数

■pthread_exit函数

■pthread_join函数

■pthread_cancel函数

■pthread_detach函数

23

=pjiirgadioin函数

■该函数用芋等,待某个线程终止1I

■函数原型.............................

#include<pthread.h>

intpthreadjoin(pthread_tthread,

void**rval_ptr);

■调用该函数的线程将一直阻塞,直到指定

的线程调用pthread_exit、从启动例程中返

回、被取消

24

PihjrgadJoin函数

intpthreadjoin(pthread_tthread,void**rval_ptr);

■返回值与参数

।A成功返回0,否则返回错误编号।||[]

>thread:需要等待的线程ID

>rval_ptr:

3■若线程从启动例程返回,rval_ptr将包含返回码1

■若线程由于pthread_exit终止,rval_ptr即pthread_exit的参数

■若线程被取消,由rval_pt「指定的内存单元就置为

PTHREAD_CANCELED

■若不关心线程返回值,可将该参数设置为NULL

25

PihjrgadJoin函数

■为什么pthread_join的第二个参数类型是指

针的指针?

I»指针的指针基本原理?传值与传指针的区别?

,>pthread_exit的一个目标是,把一个指针传递

给pthread」oin函数

Apthread」oin函数的思路是:通过参数的返回

值,将该指针值返回给pthread_join的调用者

»示州3.31।।।।।।।।

>示例3.4

26

线程的创建与终止

■pthread_create函数

■pthread_exit函数

■pthread_join函数

■pthread_cancel函数

■pthread_detach函数

27

□threadcancel函数

■线程调用该函数可以取消同一进程中的其

他线程,即让线程终止

■函数原型IIIIIIII

#include<pthread.h>

intpthread_cancel(pthread_ttid);

■参数与返回值

>tid:需要取消的线程ID

A成功返回0,出错返回错误编号

28

□threadcancel函数

■在默认情况下,pthread_cancel函数会使

得线程ID等于tid的线程,如同其调用了参

数为PTHREAD_CANCELED的

pthread_exit(示例3.5)

;■线程可以选择忽略取消方式或者控制取消

方式,将在后面讨论

■pthread_cancel并不等待线程终止,它仅

仅是提由请求

29

线程的创建与终止

■pthread_create函数

■pthread_exit函数

■pthread_join函数

■pthread_cancel函数

■pthread_detach函数

30

pthreaddetach函数

■在默认情况下,线程的终止状态会保存到

对该线程调用pthread_join

■若线程已经处于分离状态,线程的底层存

储资源可以在线程终止时立即被收回

■当线程被分离时,并不能用pthread_join函

数等待它的终止状态,止匕时pthread_join返

回EINVAL

■pthread_detach函数可以使线程进入分离

状态一

31

pthreaddetach函数

■函数原型I11,111♦

#include<pthread.h>

intpthread_detach(pthread_ttid);

■参数与返面值一

>tid:进入分离状态的线程的ID

A成功返回0,出错返回错误编号

■示例3.6

■若pthread_join比pthread_detach先调用,

也能获取到退出信息一

32

线程的创建与终止

■pthread_create函数

■pthread_exit函数

■pthread_join函数

■pthread_cancel函数

■pthread_detach函数

33

・线程的基本概念

・线程的创建与终止II

・线程创建的封装111

・线程的同步

-线程创建的再封装

■Windows消息在Linux的重现

线程创建的封装

■基于对象的封装

■面向对象的封装111

■基于模板的面向对象的封装

■基于接口的封装I11

■基于模板的面向方面的封装

■基于接口的再封装

基本对象的封装

■每次调用pthread_create很繁琐,能否简化

线程的创建工作......................

■示例3.7

>为什么使用static函数?................

|»为什么需要传递this指针?

>StartFunctionOfThread是private的,为什么能

司1够被调用?11,11,11

?A能否封装变化点?IIIIIII

36

面向对象的封装

■示例3.8

°X为什么析构是虚的?........................

।》能否封装变化点?............................

।»耦合度如何?।.........................

;下A如何理解继承关系耦合度高?

»耦合于接口、耦合于实现

*.♦■在3.8中,只能通过创建线程的方式执行业务逻辑

■若现在需要通过创建进程的方式执行业务逻辑,该

怎么办

基于模板的面向对象的封装

■示例3.9

A基于模板的静态多态................

>关键在于理清模板参数的演绎过程

基于接口的封装

■不同的线程,通常处理不同的事情,即业

务功能.......................

■如何对此变化点(执行不同业务功能)进

行封装

■示例3.10

■实现了线程具体业务功能的装配

接口的封装

■目前是以创建线程的方式执行业务功能,

如果需要创建进程的方式执行业务功能,

又当如何?

■如何对此变化点(创建不同的执行体)进

行封装?

■示例3.11

r实现了执行体的装配、业务功能的装配

■本课程的执行体创建模型,以3.11为基础

,但后续还有更多的改造

基于接口的封装

■基于接口的编程模式,更有利于装配

A在不改变处理类源代码的情况下,可以自由组

A继承是一种紧耦合的关系

.%■耦合于实现

■耦合于接口

»而基于接口的编程模式,仅耦合于接口

41

基于模板的面向方面的封装

■示例332...........................I

■为什么需要CLTypeSaver?

■为什么CLThread没有类模板参数?।।।

■当需要扩展时,即创建不同类型执行体,执行不

.同业务逻辑时,只需要新增加基类而不是派生类

■每个模板参数,即基类,代表一个方面।।

■创建线程还是进程,执行何种业务逻辑,甚至协

调器本身均可以装配

基于接口的再封装

■在3.10示例中,做到了不同执行体、不同

业务逻辑的装配,能否像3.12,做到协调

器的装配।..................

■创建完执行体之后,是否可以不立即执行

业务逻辑,这个流程能装配吗?

■示例3.13

・线程的基本概念

・线程的创建与终止II

・线程创建的封装111

.线程的同步

-线程创建的再封装

■Windows消息在Linux的重现

一线程的同步

■缓程向步加赧念.................I

、互斥量.............................

、读写锁.............................

■条件变量

45

线程同步的概念

,为什么需萋同'步.............I

A对同一个存储单元,至少存在两个执行体,其

一读该单元,另一写该单元,则需要同步,避

免不一致性

A在处理器架构中,对内存单元的修改,可能需

要多个总线周期,因此读操作和写操作有可能

交织在一起

46

线程同步的概念

■假设读操作需要

ThreadAThreadB

一个总线周期

■写操作需要两个read

总线周期

write3

■线程B和线程A冲time

write,

47

解决上述问题的方法

■使用锁,以保证共享ThreadAThreadB

存储一次只能被一个read

线程访问III

■说明获取、释放锁的

过程

read

48

线程同步的概念

ThreadAThreadBContentsofi

■通常,对一个存储

fetchiintoregister

单元的访问,要经5

(registet-5)

历三个步骤

incrementthe

contentsoffetchiintoregister

>将内存单元中的数5

theregister(register=5)

据,读入寄存器(register=6)

time

A对寄存器中的值进storethecontentsincrementthe

oftheregistercontentsof

6

行运算intoitheregister

(rcgister^6)(register06)

»将寄存器中的值,

storethecontents

写回内存单元oftheregister

intoi

■无锁时的情况(register=6)

49

线程同步的概念

■单线程的程序,需要对存储同步访问吗?

■若需要,能用锁的机制吗?

50

线程同步

■线程同步的概念

■互斥量II

■读写锁II

■条件变量

51

____________互斥量

■可以通过使用pthread的互斥接口保护数据,

1确保同一时间里只有q个线程访问数据I

■互斥量mutex,本质上就是一把锁11

I>在访问共享资源前,对互斥量进行加锁I

»在访问宛成耳释角互斥量上的四।।I

A对互斥量进行加锁后,任何其他试图再次对互

斥量加锁的线程将会被阻塞,直到锁被释放

52

互斥量的初始化

■互斥量在使用前,必须要对互斥量进行初始

,■函数原型................................

#include<pthread.h>

intpthread_mutex_init(pthread_mutex_t*mutex,

constpthread_mutexattr_t*attr);

■参数与返回值一一

>mutex:即互斥量,类型是pthread_mutex_t

1:1注意:mutex必须指向有效而内存区域

53

互斥量的初始化

intpthread_mutex_init(pthread_mutex_t*mutex,

constpthread_mutexattr_t*attr);

■参数与返回值....................I

>attr:设置互斥量的属性,通常可采用默认属性,

即可将attr设为NULL。后面再讨论互斥量的属性

»成功返回0,出错返回错误码

54

互斥量的销毁_________

■互斥量在使用完毕后,必须要对互斥量进行

销毁,以释放资源

,■函数原型.............................

#include<pthread.h>

intpthread_mutex_destroy(

pthread_mutex_t*mutex);

J参数与返回值一".

>mutex:即互斥量

A成功返回0,出错返回错误码

55

互斥量的加锁和解锁操作

■在对共享资源访问之前和访问之后,需要对

互斥量进行加锁和解锁操作

■「函数原型IIIIIIIII

#include<pthread.h>

intpthread_mutexjock(pthread_mutex_t*mutex);

Intpthread_mutex_unIock(

pthread_mutex_t*mutex);

■回忆锁的语义

56

尝试锁

■当使用pthread_mutex_lock时,若已被加锁,则

调用线程将被防塞。看殳有办法让线程不阻塞,

即实现非阻塞的语义।।।।।।

■函数景型1........................

#include<pthread.h>

intpthread_mutex_trylock(pthread_mutex_t*mutex);

・调用该函数时,若互斥量未加锁,则锁住该互斥

量,返回0;若互斥量已加锁,则函数直接返回

失败,即EBUSY

57

互斥量的操作顺序

■定义一个互斥量pthread_mutex_t

■调用pthread_mutexjnit初始化互斥量

■调用pthread_mutex_lock或者

pthread_mutex_tryplock对互斥量进行力口锁

慧操作I',"!1,।,,,

■调用pthread_mutex_unlock对互斥量解锁

・调用pthread_mutex_destroy车肖毁互斥量

■示例3.14(在示例3.11基础之上)

58

CLLOQ的改进

■2.27示例中,CLLog的实现不是线程安全

।的................................

।X写文件时的不安全।।।।।

>创建CLLog唯一对象时的不安全

a-示例3.25(在2.27基础之上)((

解决了写文件时的不安全问题,1

■示例3.26(在3.25基础之上)

>保证了多线程环境下只会创建一个对象

CLLOQ的改进________

■示例3.26存在的问题1111

ACLLog类型的对象只会被创建一次,其后都将

是读操作

1A但每次读都需要加锁、解锁,效率不高।

mA能否让后续的读操作,不再加锁

■示例427.........................................

"A双检测机制1111,111

>m_pLog为什么是volatile?

volatile变量

■volatile变量:一般在多线程中使用的比较

»例如有一个intx,有两个线程都要对其读写

»有些编译器或CPU会将x保存在寄存器中,读

的时候直接读取寄存器中的内容,而不是真实

的x在内存中的内容

A线程1,对X进行加1操作,止匕时内存中X的值为

2

A线程2想读X,结果从寄存器中读出1

»给变量加上volatile,指示程序每次读写变量都

必须从内存中读取,不要进行缓存(寄存器)

互斥量的封装_________

■每次使用互斥量都需要调用pthread_mutexjnit

和pthread_mutex_destroy卤数,能否简化

■示例3.15(在示例3.11基础之上)

互斥量的封装

■获取锁之后,一定要释放锁

■但是有时候锁的释放并不容易被控制1

■示例3.16(在示例3.11基础之上)

।»为什么主线程被阻塞?

»在mutex.Lock()和mutex_Unlock()之间若存在

复杂的函数调用,异常处理又当如何?

A每次访问临界区,都需要显示调用加锁和解锁,

能否简化

63

临界区的封装_________

■示例3.17(在示例3.11基础之上)

■无论是异常退出还是中途调用return退出,

'都能保证解锁।...................

■互斥量的不同范围,能提供不同程度的互

’》类级别的(static的互斥量)

»对象级别的(普通数据成员的互斥量)

线程同步

,庚程向步加赧念111

’・亘斥量..............

■读写锁..............

■条件变量

65

___________________________

■读写锁与互斥量类似,不过读写锁允许更高的并

I行性..................................

■互斥量只有两种状态:锁住、不加锁

■读苛锁三种状态

1>读模'式下方口锁决态.........................

7写模式下加锁状态....................

I>不加]锁状申[]][]]I[

■一次只有一个线程可以占有写模式的读写锁、但

多个线程可以同时占用读模式的读写锁

66

________________

■当读写锁是写加锁状态时,解锁之前,所

有试图对这个锁加锁的线程都会被阻塞

■当读写锁是读加锁状态时,所有试图以读

模式对它进行加锁的线程,都可以得到访

问权,但以写模式进行加锁时,它必须阻

塞直到所有的线程释放读锁

■考虑:如何在互斥量的基础之上,实现单

写多读锁?

■示例3.18(在示例3.11基础之上)

67

读写锁API

■与互斥量一样,读写锁在使用之前,必须要初始

化;使用之后,必须要销毁iiii

■函数原型..................................

#include<pthread.h>

intpthread_rwlock_init(pthread_rwlock_t*rwlock,const

pthread_rwlockattr_t*attr);

intpthread_rwlock_destroy(pthread_rwlock_t*rwlock);

■返回值

A成功返回0,否则返回错误编号

68

读写锁API

intpthread_rwlock_init(pthread_rwlock_t

*rwlock,constpthread_rwlockattr_t*attr);

intpthread_rwlock_destroy(pthread_rwlock_t

*rwlock);

・参数'............................

>rwlock:读写锁

>attr:读写锁属性,若用默认属性,可设置为

NULLo将在后面讨论相关属性

69

pthreadFW!OQK1rdiock函数

■该函数用于锁定读锁1111

■函数原型.............................

#include<pthread.h>

intpthread_rwlock_rdlock(pthread_rwlock_t

*rwlock);

■参数与返回值

>rwlock:读写锁

»成功返回0,否则返回错误编号

70

pjh迨adrwlocj<wrlock函数

■该函数用于锁定写锁1111-

■函数原型.........................

#include<pthread.h>

intpthread_rwlock_wrlock(pthread_rwlock_t

*rwlock);

■参数与返回值

>rwlock:读写锁

»成功返回0,否则返回错误编号

71

□threadrwlockunlock函数

■该函数用于解除读锁或者写锁1'

■函数原型.........................

#include<pthread.h>

intpthread_rwlock_unlock(pthread_rwlock_t

*rwlock);

■参数与返回值

>rwlock:读写锁

»成功返回0,否则返回错误编号

72

一尝试读写锁函数

-该两个函数分别用于尝试获取读锁、写锁.

■函数原型..............................11

#include<pthread.h>

intpthread_rwlock_tryrdlock(pthread_rwlock_t*rwlock);

intpthread_rwlock_trywrlock(pthread_rwlock_t*rwlock);

■参数与返回值IIIIIII!

>rwlock:读写锁

A成功返回0,否则返回错误编号。可以获取锁时,函数

返回0;否则返回错误EBUSY

73

线程同步

,庚程向步加赧念111

’・亘斥量..............

■读写锁..............

■条件变量

74

■现有一需求,线程A先执行某操作后,线程

B才能执行另一操作,该如何实现?

■条件变量与互斥量一起使用时,允许线程

以无竞争的方式等待特定条件的发生

■与互斥量类似,条件变量也需要初始化和

销毁

75

条件变量的初始化和销毁

■函数原型

#include<pthread.h>

intpthread_cond_init(pthread_cond_t*cond,

pthread_condattr_t*attr);

intpthread_cond_destroy(pthread_cond_t*cond);

■参数和返回值।।।।।।।

>cond:条件变量

.>attr:条件变量属性,若为NULL,则使用默认属性

»成功返回0,出错返回错误编号

76

一等待条件的发生

■pthread_cond_wait函数将使调用线程进入阻塞

状态,直到条件为真

■函数原型

#include<pthread.h>

intpthread_cond_wait(pthread_cond_t*cond,

pthread_mutex_t*mutex);

■参数与返回值一一

>cond:条件变量

>mutex:互斥量

A成功返回0,否则返回错误编号

77

一等待条件的发生

■为什么pthread_cond_wait需要互斥量

»在调用pthread_cond_wait前,需要使互斥量

处于锁住状态

>这样pthread_cond_waitBi数,可以以原子的

方式,将调而线程放到等待条件的线程列表上

■pthread_cond_wait函数的特殊操作

»在线程阻塞前,调用pthread_mutex_unlock

BA在线程唤醒后,调用pthread_mutex_lock।

78

一等待条件的发生

■等待线程的操作顺序1111

»调用pthread_mutex_lock

»调用pthread_cond_wait

»调用pthread_mutex_unlock

79

一使等待的条件为真

■pthread_cond_signal和

pthread_cond_broadcast可以通知等待的线程,

条件已盔满足「j1

■pthread_cond_signal唤醒某一个等待该条件的

「线程r11111111

■pthread_cond_broadcast唤醒等待该条件的所

有线程——

80

—等待条件为真

,■函数原型...........................I

#include<pthread.h>

intpthread_cond_signal(pthread_cond_t*cond);

intpthread_cond_broadcast(pthread_cond_t*);

I-参金专返回值,,,,,,,

>cond:条件变量

A成功返回0,否则返回错误编号

条件变量的封装

■示例3.19(在示例3.11基础之上)

1a为什么主线程陷入阻塞状态而不返回?

>为什么子线程加入sleep后正常?

82

___________条件变量___________

-上例由错加原I因..............

»子线程调用pthread_cond_signal后,主线程才

调用pthread_cond_wait进入阻塞状态

,A这样,主线程就一直无法被唤醒।

■解决方案

A示例3.20(在示例3.11基础之上)

83

■等待线程

>调用pthread_mutex_lock

>While(判断条件)pthread_cond_wait

■重复检查条件是由于线程可能不是被pthread_cond_signal唤

醒,可能是由信号等唤醒(futex)

>pthread_mutex_unlock

■被等待线程一।।।।।।।

>调用pthread_mutex_lock

.>修改条件।一।7।।।।।।

>调用pthread_mutex_unlock

>调用pthread_mutex_broadcast等

84

___________条件变量___________

■P0SIX.1说明:pthread_cond_broadcast

等函数的调用无需考虑锢用线”是否拥有

锁,并建议在在lock和unlock以外的区域调

।用「用什么?।।।।।।।।

[假设率统|有J线中和।线岸2((((

矗A线程1获取mutex,在进行数据处理的时候,线

程2(等待线程)也想获取mutex,但是此时被

线程1所占用,线程2进入休眠,等待mutex被

释放

85

条件变量

A线程1做完数据处理后,调用

pthread_cond_signal唤醒等待队歹U中某个线程,

在本例币也就是线程2。线程1在调用

pthread_mutex_unlocklil,因为系统调度的原

因,线程2获取使用CPU的权利,那么它就想

要开始处理数据,但是在开始处理之前,

mutex必须被获取,线程1正在使用mutex,所

以线程2被迫再次进入休眠

»然后就是线程1执行pthread_mutex_unlock()

后,线程2方能被再次唤醒厂"

»十分低效

86

事件的封装__________

■条件变量的处理比较复杂,需要有flag变量、

1固定的函数调用序列等等।iiii

'能否简化条件变量的使用..............

■封装的思路:iI..................

>Windows的事件机制

:»让一个线程等待某一个事件的发生।।

■示例3.21(在示例3.11基础之上)

>为什么m_Flag是volatile的

87

一线程的同步

■缓程向步加赧念.................I

、互斥量.............................

、读写锁.............................

■条件变量

88

・线程的基本概念

・线程的创建与终止II

・线程创建的封装111

.线程的同步

I线程创建的再封装

■Windows消息在Linux的重现

线程创建的再封装

■一个CLThread类的对象,应该只对应一个

。线程.............................

■用户如果按照如下方式使用,会有什么后

果?

CLThread*p=..................;

p->Run();

p->Run();

线程创建的再封装

■前一次创建的线程,可能无法再被控制。

例如:m_ThreadlD仅为刚创建线程的ID,

调用WsdtForDeath,将无法等待上一个线

程的结束।।।।।।।।

■如何解决,以防止在一个CLThread对象上

,多次调用Run方法?一‘||

■示例3.22(在示例3.11基础之上)

线程创建的再封装

■通常情况下,CLThread类及

CLExecutiveFunctionProvider派生类的对

象,应从堆中分配。为什么不是栈上?

■从堆中分配,就需要显式的释放,能否简

化?能否让类的使用者,只分配对象,而

不用调用delete?

■示例3.23(在示例3.22基础之上)

线程创建的再封装

■在有些情况下,新线程的创建者,可能不

需要等待新线程死亡,即不调用

WaitForDeath

■这样3.23的释放对象方式将失效।।।

■示例3.24(在示例3.23基础之上)

»增加一个标识,让用户指定他是否需要等待新

线程死亡

线程创建的再封装

1

■产生异常的原因在于:111'V

>新线程在主线程调用WaitForDeath之前就已经退出了

>甚至有可能在主线程刚调用完pthread_create之后,

就退出了

■解决办法:।।।।।।।।।

>需要保证在Run方法退出之前,新线程不会死亡

A若用户选择了不等待新线程死亡,则他就不应该再调

fflWaitForDeath

»另外,从语义上讲,Run方法返回前,应保证新线程

确实已经被创建了

A示例3.28(在示例3.24基础之上)

・线程的基本概念

・线程的创建与终止II

・线程创建的封装111

.线程的同步

g线程创建的再封装

■Windows消息在Linux的重现

Windows消息在Linux的重现

■在windows中,进行线程之间的通信十分

简便。Windows系统为每个线程都配备了

二个消息队列..................

■调用PostThreadMessage函数,可以向一

1个线程发送消息।।।।।।

■调用GetMessage函数,可以从消息队列中

获取一个消息

生产者/消费者模型

■消息队列及机制,是一种典型的生产者/消

费者模型应用。

■发送消息的线程(生产者):将消息投入

到消息队列后,即返回

■消息的接收线程(消费者):一直处于阻

塞等待状态,直到有线程将消息投入到消

息队列中

■我们的目标:建立一个自定义的消息队列

,以实现同一进程内线程之间的通信

Windows消息在Linux的重现

■梏息的封装111111-

■自定义消息队列的实现1,11

■消息循环机制的封装11111

■创建线程与消息循环的结合III

■简单的名字服务..................

■使用的简化

Windows消息在Linux的重现

■消息的封装.....................♦

■自定义消息队列的实现1,11

■消息循环机制的封装..............

■创建线程与消息循环的结合,11

■简单的名字服务..................

■使用的简化

消息的封装__________

■Windows的线程消息:

BOOLWINAPIPostThreadMessage(

DWORDidThread,

UINTMsg,

WPARAMwParam,

LPARAMIParam

);

■Windows的消息包含了线程ID,以及两个自定义

参数,不符合面向对象的思想

■需要建立起有关消息的继承体系

消息的继承体系

■示例3.29

■CLMessage是对消息的抽象,仅包含了消

息的ID

■线程之间发送的具体消息,需要从।।

CLMessage派生,并定义属于该消息自己

的参数

Windows消息在Linux的重现

■消息的封装111111-

■自定义消息队列的实现||||

■消息循环机制的封装..............

■创建线程与消息循环的结合111

■简单的名字服务..................

■使用的简化

自定义消息队列的实现

■队列本身采用STL库中的queue类,该类型

的队列能自动增长................

■必须以线程安全的方式,将消息放入到队

列中,然后通知等待的线程消息已到达

■通知的机制可以采用CLEvent类,但3.21

示例中的CLEvent必须改造

■示例3.30

自定义消息队列的实现

■CLMessageQueue的实现

■示例3.31

■在上例中,采用STL的queue实现了消息队

歹U]।।।।।।।।।।

■也可以采用其他方式实现,例如管道、网

络通信、自定义的队列等等

■如何封装这一变化点?它们之间是否有共

通的部分(均为生产者/消费者模型应用)

Windows消息在Linux的重现

■消息的封装111111-

■自定义消息队列的实现||||

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论