2022年由浅入深Linux下pthread线程库介绍汇编 .pdf
《2022年由浅入深Linux下pthread线程库介绍汇编 .pdf》由会员分享,可在线阅读,更多相关《2022年由浅入深Linux下pthread线程库介绍汇编 .pdf(15页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Linux下 pthread线程库介绍1、概述多线程程序作为一种多任务、并发的工作方式,有以下的优点:1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。2) 使多 CPU 系统更加有效。操作系统会保证当线程数不大于CPU 数目时,不同的线程运行于不同的CPU 上。3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。Linux 下最
2、常用的是遵循POSIX 标准的 pthread 线程库。 pthread 的实现是通过系统调用clone ()这一 Linux 特有的系统调用来实现。2、线程创建与结束1)pthread_t 线程的标识符类型,pthread_t在头文件 /usr/include/bits/pthreadtypes.h中定义:typedef unsigned long int pthread_t; 2)pthread_create thread_create用来创建一个线程,它的原型为:extern int pthread_create _P (pthread_t *_thread, _const pthrea
3、d_attr_t *_attr, void *(*_start_routine) (void *), void *_arg); 第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread 不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为 0 则说明创建线程失败,常见的错误返回代码为EAGAIN 和 EINVAL 。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二
4、个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 15 页 - - - - - - - - - 3)pthread_join 函数 pthread_join用来等待一个线程的结束。函数原型为:extern int pthread_join _P (pthread_t _th, void *_thread_return); 第一个参数为被等待的线程标识符,第
5、二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。4)pthread_exit 线程除了正常执行结束外,还可以通过函数pthread_exit来结束它, pthread_exit的函数原型为:extern void pthread_exit _P (void *_retval) _attribute_ (_noreturn_); 唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是 NULL ,这个值将被传递给threa
6、d_return 。最后要说明的是, 一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH 。3 修改线程的属性属性结构为 pthread_attr_t,在头文件/usr/include/pthread.h中定义。属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M 的堆栈、与父进程同样级别的优先级。1) 关于线程的绑定线程的
7、绑定,牵涉到另外一个概念:轻进程(LWP :Light Weight Process )。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的绑在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为 CPU 时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
8、设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS (非绑定的)。下面的代码即创建了一个绑定的线程。#include 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 15 页 - - - - - - - - - pthread_attr_t attr; pthread_t tid; /*初
9、始化属性值,均设为默认值*/ pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_create(&tid, &attr, (void *) my_function, NULL); 2)关于线程的状态:分离态/非分离态线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中, 我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join ()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是
10、这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate )第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交
11、给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait ()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。3)关于线程的优先级线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setsch
12、edparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。线程的优先级取值范围为-2020, 值越大,其优先级越低,缺省值为0,这个参数仅当调度策略为实时(即SCHED_RR或 SCHED_FIFO )时才有效,在运行时通过pthread_setschedparam()函数来改变:下面即是一段简单的例子。#include #include pthread_attr_t attr; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 15 页 - - -
13、 - - - - - - pthread_t tid; sched_param param; int newprio=20; pthread_attr_init(&attr); pthread_attr_getschedparam(&attr, ?m); param.sched_priority=newprio; pthread_attr_setschedparam(&attr, ?m); pthread_create(&tid, &attr, (void *)myfunction, myarg); 4 线程的数据处理和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据
14、段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个拷贝(除非使用不同的数据段)。在函数中声明的静态变量常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别的线程可能调用此函数并修改了这一段数据。在进程党国享的变量必须用关键字volatile 来定义,这是为了防止编译器在优化时(如gcc 中使用 -OX 参数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。
15、1) 线程数据在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据( TSD: Thread-Specific Data )。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见的。例如我们常见的变量 errno ,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在A 线程里输出的很可能是B 线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线
16、程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。和线程数据相关的函数主要有4 个:创建一个键;为一个键指定线程数据;从一个键读取线程数据;删除键。创建键的函数原型为:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 15 页 - - - - - - - - - extern int pthread_key_create _P (pthread_key_t *_key,void (*_destr_functi
17、on) (void *); 第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。这个函数常和函数pthread_once (pthread_once_t*once_control, void (*initroutine) (void) 一起使用, 为了让这个键只被创建一次。函数 pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这个函数,以后的调用将被它忽略。在下面的例子中,我们创建一个键,并将它和某个数据相关联。我们要定义一个函数create
18、Window ,这个函数定义一个图形窗口(数据类型为Fl_Window *,这是图形界面开发工具FLTK 中的数据类型)。由于各个线程都会调用这个函数,所以我们使用线程数据。/* 声明一个键 */ pthread_key_t myWinKey; /* 函数createWindow */ void createWindow ( void ) Fl_Window * win; static pthread_once_t once= PTHREAD_ONCE_INIT; /* 调用函数createMyKey ,创建键 */ pthread_once ( & once, createMyKey) ;
19、/*win 指向一个新建立的窗口*/ win=new Fl_Window( 0, 0, 100, 100, MyWindow); /* 对此窗口作一些可能的设置工作,如大小、位置、名称等*/ setWindow(win); /* 将窗口指针值绑定在键myWinKey 上*/ pthread_setpecific ( myWinKey, win); /* 函数createMyKey ,创建一个键,并指定了destructor */ void createMyKey ( void ) pthread_keycreate(&myWinKey, freeWinKey); /* 函数freeWinKey
20、 ,释放空间 */ 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 15 页 - - - - - - - - - void freeWinKey ( Fl_Window * win) delete win; 这样,在不同的线程中调用函数createMyWin ,都可以得到在线程内部均可见的窗口变量,这个变量通过函数 pthread_getspecific得到。在上面的例子中, 我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函
21、数的原型如下:extern int pthread_setspecific _P (pthread_key_t _key,_const void *_pointer); extern void *pthread_getspecific _P (pthread_key_t _key); 这两个函数的参数意义和使用方法是显而易见的。要注意的是,用pthread_setspecific为一个键指定新的线程数据时,必须自己释放原有的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键,这个键占用的内存将被释放,但同样要注意的是,它只释放键占用的内存,并不释放该键关联的线程
22、数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的 destructor 函数。线程数据的释放必须在释放键之前完成。2) 互斥锁互斥锁用来保证一段时间内只有一个线程在执行一段代码。我们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息或没有信息。void reader_function ( void ); void writer_function ( void ); char buffer; int buffer_has_item=0; pthread_mutex_t mutex;
23、struct timespec delay; void main ( void ) pthread_t reader; /* 定义延迟时间 */ delay.tv_sec = 2; delay.tv_nec = 0; /* 用默认属性初始化一个互斥锁对象*/ 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 15 页 - - - - - - - - - pthread_mutex_init (&mutex,NULL); pthread_create(&reader, pth
24、read_attr_default, (void *)&reader_function), NULL); writer_function( ); void writer_function (void) while(1) /* 锁定互斥锁 */ pthread_mutex_lock (&mutex); if (buffer_has_item=0) buffer=make_new_item( ); buffer_has_item=1; /* 打开互斥锁 */ pthread_mutex_unlock(&mutex); pthread_delay_np(&delay); void reader_fu
25、nction(void) while(1) pthread_mutex_lock(&mutex); if(buffer_has_item=1) consume_item(buffer); buffer_has_item=0; pthread_mutex_unlock(&mutex); pthread_delay_np(&delay); 这里声明了互斥锁变量mutex ,结构 pthread_mutex_t为不公开的数据类型,其中包含一个系统分配的属名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - -
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年由浅入深Linux下pthread线程库介绍汇编 2022 由浅入深 Linux pthread 线程 介绍 汇编
限制150内