《LINUX环境高级编程(六)课件》由会员分享,可在线阅读,更多相关《LINUX环境高级编程(六)课件(25页珍藏版)》请在金锄头文库上搜索。
1、LINUX环境高级编程,中国地质大学(武汉)计算机学院 朱静,中国地质大学(武汉)计算机学院,线 程,避免死锁,线程的同步,线程创建与终止,线程概念的引入,多线程,并发执行的进程具有两个基本的属性 (1)资源分配属性。操作系统以进程为单位分配虚地址空间、主存和其它系统资源; (2)调度属性。操作系统需要为进程分配调度CPU时间。 这两个基本属性使进程成为并发执行的基本单位。在一些OS中,大多数UNIX系统、Linux等,进程同时具有这两个属性。而另一些OS中,象WinNT、Solaris、OS2、Mac OS等,这两个属性由OS独立处理。 为了区分两个属性,资源分配单位称为进程(或任务),调度
2、单位称为线程。,线程概念的引入,中国地质大学(武汉)计算机学院,线程 进程内一个CPU执行单元或一个可调度实体 线程只拥有一点在运行中必不可省的资源(线程ID,程序计数器、一组寄存器、栈和线程私有数据),但它可与同属一个进程的其它线程共享进程拥有的全部资源。 在多线程的操作系统中,处理机调度的基本单位是线程。一个进程可以有多个线程,而且至少有一个可执行线程。,中国地质大学(武汉)计算机学院,线程概念的引入,进程和线程的关系 线程是进程的一个组成部分。每个进程创建时只有一个线程,需要时可创建其他线程。 进程的多线程都在进程的地址空间活动。 资源是分给进程的,不是分给线程的。线程在执行中需要资源时
3、,可从进程资源中划分。 处理机调度的基本单位是线程,线程之间竞争处理机。真正在CPU上运行的是线程。 线程在执行时,需要同步。,中国地质大学(武汉)计算机学院,线程概念的引入,进程 控制块 PCB,用户地址空间,线程 控制块 TCB 用户栈 核心栈,线程 控制块 TCB 用户栈 核心栈,线程 控制块 TCB 用户栈 核心栈,多线程进程模块,线程A,线程B,线程C,多线程是OS在一个进程内支持多个线程的能力。,中国地质大学(武汉)计算机学院,线程概念的引入,单线程与多线程的比较,Thread Control Block,User Stack,User Stack,Kernel Stack,Ker
4、nel Stack,User Address Space,User AddressSpace,Process Control Block,Process Control Block,Thread,Single-Threaded Process Model,Multithreaded Process Model,Thread Control Block,User Stack,Kernel Stack,Thread,Thread Control Block,User Stack,Kernel Stack,Thread,中国地质大学(武汉)计算机学院,线程的特征 创建线程比创建进程快,且节省开销。
5、一个进程至少要有一个可执行线程,可以有多个线程。 一个线程可以创建它所需的其他线程。 一个线程可以有就绪,等待,运行等状态。 进程可创建多个线程来执行同一个程序的不同部分,方便而有效地实现并行性。 每个线程有一个线程ID,进程ID在整个系统中是唯一的,但线程ID只在它所属的进程环境中有效。,中国地质大学(武汉)计算机学院,线程概念的引入,创建线程 #include pthread_t pthread_create(pthread_t *restrict thread,const pthread_attr_t *restrict attr,void *(*start_routine)(void
6、*),void *restrict arg); 调用成功返回由thread指向的线程ID,attr参数用于指定线程属性,attr设为NULL表示创建默认属性的线程。新建线程从start_routine函数的地址开始运行,arg是传递给该函数的参数。 例: 创建新线程 thread1.c (LP P420) 打印线程ID threadid.c $gcc thread1.c o thread1 lpthread 注意:包含线程的C程序在编译时加-lpthread参数,中国地质大学(武汉)计算机学院,线程创建与终止,线程ID相关系统调用 #include pthread_t pthread_self
7、(void);获得线程自身ID int pthread_equal(pthread_t tid1,pthread_t tid2);比较两个线程ID是否相等,线程创建与终止,中国地质大学(武汉)计算机学院,线程的终止 如果进程中的任一线程调用了exit,Exit或_exit,整个进程将会终止。 如果信号的默认动作是终止进程,那么把该信号发送到线程会终止整个进程 单个线程可以通过下列三种方式在不终止整个进程的情况下终止: 1,线程只是从函数调用中返回; 2,线程被同一进程中的其他线程调用pthread_cancel取消; 3,线程调用pthread_exit终止;,中国地质大学(武汉)计算机学院,
8、线程创建与终止,#include void pthread_exit(void *rval_ptr); rval_ptr是一个无类型指针,与传递给pthread_create的单个参数类似。进程中的其他线程可以通过调用pthread_join函数访问到这个指针。 int pthread_join(pthread_t thread,void *rval_ptr); 调用这个函数的线程将一直被挂起,直到指定的线程调用pthread_exit、或从函数执行完后返回或被取消。 例:thread1.c (LP P420) 取消线程 thread7.c (LP P436) 获得线程退出状态 exitsta
9、tus.c,中国地质大学(武汉)计算机学院,线程创建与终止,初始化线程属性 #include void pthread_attr_init(pthread_attr_t *attr) 该函数用于使用参数attr中的属性来初始化线程属性。该函数必须在pthread_create()函数之前被调用。 typedef struct int _detachstate; /线程的分离状态; int _schedpolicy; /线程的调度策略; int _inheritsched; /线程属性的继承性 int _scope; /线程作用域 int _stackaddr_set; size_t _guar
10、dsize; void * _stackaddr; pthread_attr_t;,线程创建与终止,线程的分离状态 线程的分离状态决定了线程如何结束。 DETACHED:分离状态。处于分离状态的线程退出时会释放所占用的资源。 JOINABLE:非分离状态(默认)。线程所属的进程没有退出之前,不会释放线程所占用的资源。非分离状态的线程可以通过调用pthread_join函数等待其他线程结束,这样线程结束后会释放自己占用的资源。 pthread_attr_setdetachstate()设置分离状态属性函数 pthread_attr_getdetachstate()获取分离状态属性函数,中国地质大
11、学(武汉)计算机学院,线程创建与终止,设置和获得线程属性函数 pthread_attr_setscope() pthread_attr_getscope() pthread_attr_setinheritsched() pthread_attr_getinheritsched() pthread_attr_setschedpolicy() pthread_attr_getschedpolicy() pthread_attr_setschedparam() pthread_attr_getschedparam() 例:设置分离状态 thread5.c (LP P433),中国地质大学(武汉)计算
12、机学院,线程创建与终止,信号量和互斥量 通过信号量和互斥量可以控制线程的交叉执行和访问代码临界区域 信号量是一个特殊类型的变量,可以被增加和减少,linux保证对其的关键访问是原子操作。 #include int sem_init(sem_t *sem,int pshared,unsigned int value) 初始化由sem指向的信号量对象;pshared设置为0表示是当前进程的局部信号量,不在多个进程间共享;value值为信号量初值,通常为0; int sem_wait(sem_t *sem) /信号量值减1 int sem_post(sem_t *sem) /信号量值加1 int s
13、em_destroy(sem_t *sem) /销毁信号量 例:全局变量控制线程切换效率低下。 thread2.c (LP P422) 例:信号量控制线程的同步。thread3.c (LP P424),线程的同步,中国地质大学(武汉)计算机学院,互斥量用pthread_mutex_t数据类型来表示。互斥量用于锁住某个对象,控制每次只有一个线程能够访问互斥量保护的对象; 使用互斥量之前,必须首先通过把它设置为常量PTHREAD_MUTEX_INITIALIZER(静态分配的互斥量)或者通过调用pthread_mutex_init函数进行初始化。如果动态地分配互斥量(例如通过malloc函数),那
14、么在释放内存前需要调用pthread_mutex_destroy函数销毁互斥量 例:信号量的局限 thread3a.c (LP P426) 例:使用互斥量 thread4.c (LP P428),中国地质大学(武汉)计算机学院,线程的同步,#include int pthread_mutex_init(prhtread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); 要用默认的属性初始化互斥量,只需把attr设为NULL。 int pthread_mutex_destroy(pthread_mutex_t *
15、mutex); 对互斥量进行加锁和解锁 int pthread_mutex_lock(pthread_mutex_t *mutex); /加锁 int pthread_mutex_trylock(pthread_mutex_t *mutex); /测试锁 int pthread_mutex_unlock(pthread_mutex_t *mutex); /解锁,中国地质大学(武汉)计算机学院,线程的同步,对互斥量进行加锁要调用pthread_mutex_lock;如果互斥量已经上锁,则调用线程将阻塞直到互斥量被解锁。 对互斥量进行解锁要调用pthread_mutex_unlock; 如果线程不
16、希望被阻塞,可以使用pthread_mutex_trylock尝试对互斥量进行加锁,如果调用pthread_mutex_trylock时互斥量处于末锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则pthread_mutex_trylock调用失败,不能锁住互斥量,返回EBUSY。 例:使用互斥量保护数据结构 mutex1.c,中国地质大学(武汉)计算机学院,线程的同步,如果线程试图对同一个互斥量加锁两次,它自身就会陷入死锁状态。 使用互斥量时,其他更不明显的方式也能产生死锁,例如:程序中使用多个互斥量时,如果允许一个线程一直占有一个互斥量,并且在试图锁住第二个互斥量时处于阻塞状态,但占有第二个互斥量的线程也在试图锁住第一个互斥量,这时就会发生死锁。 死锁避免: 1,可以通过小心地控制互斥量加锁的顺序来避免死锁的发生。 2,使用pthread_mutex_trylock避免死锁。,避免死锁,通过小心地控制互斥量加锁的顺序来避