您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业文化 > LINUX环境高级编程-线程.
线程的概念线程的创建和操纵线程的同步代码演示主要内容POSIX线程(POSIXthreads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API,定义了一套C语言的类型、常量、函数,以pthread.h头文件和一个线程库实现。POSIX线程具有很好的可移植性,使用pthreads编写的代码可运行于类Unix操作系统(Unix、Linux、MacOSX等)中,Windows操作系统也有其移植版pthreads-win32。3LinuxThreads项目最初将多线程的概念引入了Linux,但是LinuxThreads并不完全遵守POSIX线程标准,并且存在设计的一些局限性;为了改进Linux线程性能,IBM投资开发NGPT(Next-GenerationPOSIXThreads)项目,RedHat主导本地化POSIX线程库(NativePOSIXThreadLibrary,简称为NTPL)项目。大部分现代Linux发行版都预装了LinuxThreads或NPTL,要查看您的系统上正在使用的是哪个线程库,请运行下面的命令:◦$getconfGNU_LIBPTHREAD_VERSION◦这会产生类似于下面的输出结果:◦NPTL0.34◦或者:◦linuxthreads-0.10程序(program):是存放在磁盘文件中的可执行文件。使用6个exec函数中的一个由内核将程序读入存储器,并使其执行。进程(process):是资源管理的最小单位,是程序的执行实例,是动态过程。有些操作系统把任务和进程同等看待,认为任务是一个动态过程,即执行任务体的动态过程。线程(thread):是程序执行的最小单位,比进程更小的、能独立运行和调度的基本单元,并以此来提高程序并行执行的程度。5进程存储空间布局进程管理着资源,如文件、内存、CPU等;进程的所有信息对该进程的所有线程都是共享的,包括:可执行的程序文本、程序的全局内存、堆内存、文件描述符等。线程独有的包括:线程ID、寄存器值、栈、信号屏蔽字、errno值、线程私有数据。7一个进程可以拥有多个线程;一个进程至少需要一个线程作为它的指令执行体;典型的UNIX进程可以看成是只有一个线程的进程。Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。81、线程开销更小。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右。2、线程间通信更方便、快捷。同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。3、使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。4、改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。同进程一样,每个线程也有一个线程ID进程ID在整个系统中是唯一的,但线程ID不同,线程ID只在它所属的进程环境中有效线程ID的类型是pthread_t,在Linux中的定义:◦在/usr/include/bits/pthreadtypes.h中◦typedefunsignedlongintpthread_t;10pthread_self函数可以使调用线程获取自己的线程ID函数原型#includepthread.hpthread_tpthread_self();返回调用线程的线程ID11Linux中使用整型表示线程ID,而其他系统则不一定FreeBSD5.2.1、MacOSX10.3用一个指向pthread结构的指针来表示pthread_t类型。为了移植性,在比较两个线程ID是否相同时,可以使用pthread_equal函数12该函数用于比较两个线程ID是否相同函数原型#includepthread.hintpthread_equal(pthread_ttid1,pthread_ttid2);若相等则返回非0值,否则返回013进程原语线程原语描述forkpthread_create创建新的控制流exitpchread_exit从现有的控制流中退出waitpidpthread_join从控制流中得到退出状态atexitpthread_cleanup_push注册在退出控制流时调用的函数getpidpthread_self获取控制流的IDabortpthread_cancel请求控制流的非正常退出pthread_create函数用于创建一个线程函数原型#includepthread.hintpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);参数与返回值◦thread:当pthread_create成功返回时,该函数将线程ID存储在thread指向的内存区域中15参数与返回值◦attr:用于定制各种不同的线程属性,将在后面部分讨论。通常可设为NULL,采用默认线程属性◦start_routine:线程的入口函数,即新创建的线程从该函数开始执行。该函数只有一个参数,即arg,返回一个指针◦arg:作为start_routine的第一个参数◦成功返回0,出错时返回各种错误码16新创建的线程,将继承调用pthread_create函数的线程的信号屏蔽字,但新线程的未决信号集将被清除。17进程中任一线程调用exit、_Exit、_exit,都会导致整个进程终止;当线程接收到信号,若信号的处理动作是终止进程,则进程将被终止(后面分析信号与线程的交互);如何只终止某个线程,而不终止整个进程?18单个线程的三种退出方式◦线程从启动例程中返回,返回值是线程的退出码◦线程被同一进程中的其他线程取消◦线程调用pthread_exit函数19该函数让线程退出#includepthread.hvoidpthread_exit(void*rval_ptr);参数◦rval_ptr:与线程的启动函数类似,该指针将传递给pthread_join函数20该函数用于等待某个线程终止函数原型#includepthread.hintpthread_join(pthread_tthread,void**rval_ptr);调用该函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回、被取消21返回值与参数◦成功返回0,否则返回错误编号◦thread:需要等待的线程ID◦rval_ptr:若线程从启动例程返回,rval_ptr将包含返回码若线程由于pthread_exit终止,rval_ptr即pthread_exit的参数若线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED若不关心线程返回值,可将该参数设置为NULL22线程调用该函数可以取消同一进程中的其他线程,即让线程终止函数原型#includepthread.hintpthread_cancel(pthread_ttid);参数与返回值◦tid:需要取消的线程ID◦成功返回0,出错返回错误编号23在默认情况下,pthread_cancel函数会使得线程ID等于tid的线程,如同其调用了参数为PTHREAD_CANCELED的pthread_exit线程可以选择忽略取消方式或者控制取消方式,将在后面讨论pthread_cancel并不等待线程终止,它仅仅是提出请求24当线程终止时,可以调用自定义的线程清理处理函数,进行资源释放等操作。类似于atexit函数。线程可以注册多个清理处理函数,这些函数被记录在栈中,它们的执行顺序与它们的注册顺序相反。线程清理处理函数的注册:#includepthread.hvoidpthread_cleanup_push(void(*rtn)(void*),void*arg);25参数◦rtn:清理函数,无返回值,一个类型为指针的参数◦arg:当清理函数被调用时,arg将传递给清理函数清理函数被调用的时机◦调用pthread_exit时◦响应取消请求时◦以非0参数调用pthread_cleanup_pop时26pthread_cleanup_push必须和pthread_cleanup_pop成对出现,而且出现的地方必须在同一个作用域内函数原型◦#includepthread.h◦voidpthread_cleanup_pop(intexecute);27在默认情况下,线程的终止状态会保存到对该线程调用pthread_join;pthread_detach函数可以使线程进入分离状态;若线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即被收回;当线程被分离时,并不能用pthread_join函数等待它的终止状态,此时pthread_join返回EINVAL。28函数原型#includepthread.hintpthread_detach(pthread_ttid);参数与返回值◦tid:进入分离状态的线程的ID◦成功返回0,出错返回错误编号29代码演示线程同步的概念互斥量死锁读写锁条件变量31为什么需要同步◦对同一个存储单元,至少存在两个执行体,其一读该单元,另一写该单元,则需要同步,避免不一致性◦在处理器架构中,对内存单元的修改,可能需要多个总线周期,因此读操作和写操作有可能交织在一起32假设读操作需要一个总线周期写操作需要两个总线周期线程B和线程A冲突33使用锁,以保证共享存储一次只能被一个线程访问说明获取、释放锁的过程34通常,对一个存储单元的访问,要经历三个步骤◦将内存单元中的数据,读入寄存器◦对寄存器中的值进行运算◦将寄存器中的值,写回内存单元无锁时的情况35可以通过使用pthread的互斥接口保护数据,确保同一时间里只有一个线程访问数据互斥量mutex,本质上就是一把锁◦在访问共享资源前,对互斥量进行加锁◦在访问完成后释放互斥量上的锁◦对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞,直到锁被释放36互斥量在使用前,必须要对互斥量进行初始化函数原型#includepthread.hintpthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutexattr_t*attr);参数与返回值◦mutex:即互斥量,类型是pthread_mutex_t◦注意:mutex必须指向有效的内存区域◦attr:设置互斥量的属性,通常可采用默认属性,即可将attr设为NULL。后面再讨论互斥量的属性◦成功返回0,出错返回错误码37互斥量在使用完毕后,必须要对互斥量进行销毁,以释放资源函数原型#includepthread.hintpthread_mutex_destroy(pthread_mutex_t*mutex);参数与返回值◦mutex:即互斥量◦成功返回0,出错返回错误码38在对共享资源访问之前和访问之后,需要对互斥量进行加锁和解锁操作函数原型#includepthread.hintpthread_mutex_lock(pthread_mutex_t*mutex);intpthread_mutex_unlock(pthread_mutex_t*mutex);39当使用pthread_mutex_lock时,若已被加锁,则调用线程将被阻塞。有没有办法让线程不阻塞,即实现非阻塞的语义函数原型#includepthread.hintpthread_mutex_trylock(pthread_mutex_t*mutex);调
本文标题:LINUX环境高级编程-线程.
链接地址:https://www.777doc.com/doc-2884933 .html