第7章 ARM Linux多线程开发实例(2010-5-13)_第1页
第7章 ARM Linux多线程开发实例(2010-5-13)_第2页
第7章 ARM Linux多线程开发实例(2010-5-13)_第3页
第7章 ARM Linux多线程开发实例(2010-5-13)_第4页
第7章 ARM Linux多线程开发实例(2010-5-13)_第5页
已阅读5页,还剩96页未读 继续免费阅读

下载本文档

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

文档简介

第7章ARMLinux多线程开发实例

7.1

Linux多线程相关API

7.2信号灯

7.3互斥量

7.4条件变量

番禺职业技术学院番禺职业技术学院

7.1

Linux多线程相关API

Linux有多线程开发的Pthread

库支持。最基本概念:线程、互斥锁、条件。①线程操作又分线程的创建、退出、等待。②互斥锁则包括4种操作:创建、销毁、加锁和解锁。③条件操作有5种操作:创建、销毁、触发、广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。表7.1.线程函数列表7.1.1线程的创建1.pthread_create函数pthread_create用于创建一个新的线程。其函数原型为:#include

<pthread.h>int

pthread_create(pthread_t

*restrict

tidp,const

pthread_attr_t

*restrict

attr,

void

*(*start_rtn)(void),void

*restrict

arg);返回值:若是成功建立线程返回0,否则返回错误的编号。函数说明:第1个参数为指向要创建的线程的线程id指针,第2个参数用来设置线程属性,第3个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,函数thread不需要参数,所以最后一个参数设为空指针。第2个参数也设为空指针,这样将生成默认属性的线程。当创建线程成功,函数返回0,否则失败,常见的错误返回代码为EAGAIN和EINVAL。EAGAIN表示系统限制创建新的线程,例如线程数目过多了;EINVAL表示第2个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。

例7.1线程的创建(gettid.c)#include<pthread.h>voidprintids(constchar*s){printf(“%s

pid:%u

tid:%u\n“,getpid(),pthread_self());}void*thr_fn(void*arg){printf(“newthread:“);}

intmain(){interr;pthread_t

tid;err=pthread_create(&tid,NULL,thr_fn,NULL);if(err!=0)

printf(“can’tcreatethread:%s\n”,strerror(err));printids(“mainthread:”);sleep(1);exit(0);}

进程的编译需要要加上参数–lpthread,否则提示找不到函数的错误。具体编译方法是:gcc–lpthread–ogettid

gettid.c运行结果为mainthread:pid14954tid134529024newthread:pid14954tid1345300487.1.2线程的终止1.pthread_join

函数pthread_join

函数用来等待一个线程的结束。其函数原型为:#include<pthread.h>int

pthread_join(pthread_t

thread,void**rval_ptr);返回值:若函数调用成功返回0,否则返回错误编号。第1个参数为被等待的线程标识符,第2个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。

2.pthread_exit

函数

线程是依进程而存在的,当进程终止时,线程也就终止了。当然也有在不终止整个进程的情况下停止它的控制流。(1)线程只是从启动例程中返回,返回值是线程的退出码。(2)线程可以被同一进程中的其他线程取消。(3)线程调用pthread_exit.

pthread_exit

:是一个线程的结束。两种途径:当函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit

来实现。其函数原型为:

voidpthread_exit(void*__retval)

唯一的参数是函数的返回代码,只要pthread_join

中的第二个参数thread_return

不是NULL,这个值将被传递给thread_return。需要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join

的线程则返回错误代码ESRCH。

例7.2线程终止#include<pthread.h>#include<string.h>void*thr_fn1(void*arg){printf(“thread1returning\n”);return((void*)1);}void*thr_fn2(void*arg){printf(“thread2exiting\n”);return((void*)2);}intmain(){pthread_ttid1,tid2;void*tret;pthread_create(&tid1,NULL,thr_fn1,NULL);pthread_create(&tid2,NULL,thr_fn2,NULL);pthread_join(tid1,&tret);

printf(“thread1exitcode%d\n”,(int)tret);pthread_join(tid2,&tret);

printf(“thread2exitcode%d\n”,(int)tret);exit(0);}运行结果是:thread1returningthread2exitingthread1exitcode1thread2exitcode23.pthread_detach函数pthread_detach函数是用于使线程进入分离状态。其函数原型为:#include<pthread.h>int

pthread_detach(pthread_t

tid);返回值:若函数调用成功返回0,否则返回错误编号。在默认情况下,线程的终止状态会保存到对该线程调用pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即被收回。当线程被分离时,并不能用pthread_join函数等待它的终止状态。对分离状态的线程进行pthread_join的调用会产生失败,返回EINVAL。

4.pthread_cancel函数

pthread_cancel函数是用于取消同一进程中的其他线程。其函数原型为:#include<pthread.h>int

pthread_cancel(pthread_t

tid);返回值:若函数调用成功返回0,否则返回错误编号。在默认的情况下,pthread_cancel函数会使由tid标识的线程的行为表现为如同调用了参数为PTHEAD_CANCELED的pthread_exit函数,但是,线程可以选择忽略取消方式和控制取消方式。pthread_cancel并不等待线程终止,它仅仅提出请求。5.pthread_cancel_push/pthread_cancel_push_pop函数pthread_cancel_push/pthread_cancel_pop是线程清理处理程序。其函数原型为:#include<pthread.h>voidpthread_cancel_push(void(*rtn)(void*),void*arg);voidpthread_cancel_pop(intexecute);参数:rtn为处理程序入口地址,arg为传递给处理函数的参数。线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说它们的执行顺序与它们注册时的顺序相反。注意:如果线程是通过从他的启动例程中返回而终止的,它的处理程序就不会调用。还要注意清理处理程序是按照与它们安装时相反的顺序调用的。例7.3线程的取消#include<pthread.h>#include<stdio.h>voidcleanup(void*arg){printf(“cleanup:%s\n”,(char*)arg);}void*thr_fn(void*arg)/*线程入口地址*/{printf(“threadstart\n”);pthread_cleanup_push(cleanup,”threadfirsthandler”);/*设置第一个线程处理程序*/pthread_cleanup_push(cleanup,”thread

secondhandler”);/*设置第二个线程处理程序*/printf(“threadpushcomplete\n”);pthread_cleanup_pop(0);/*取消第一个线程处理程序*/pthread_cleanup_pop(0);/*取消第二个线程处理程序*/}intmain(){pthread_t

tid;void*tret;

pthread_creat(&tid,NULL,thr_fn,(void*)1);/*创建一个线程*/

pthread_join(tid,&tret);/*获得线程终止状态*/

ptinrf(“threadexitcode%d\n”,(int)tret);}7.1.3线程的标识每个进程有一个进程ID,每个线程也有一个线程ID,进程ID在整个系统中是唯一的,但线程不同,线程ID只在它所属的进程环境中有效。线程ID用pthread_t数据类型来表示,实现的时候可以用一个结构来代表pthread_t数据类型,所以可以移植的操作系统不能把它作为整数处理。1.pthread_self函数函数用于获取自身线程的id。其函数原型为:#include<pthread.h>pthread_t

pthread_self(void);返回值:调用线程的线程id。2.pthread_equal函数pthread_equal函数用于测试两个线程号ID是否相同。其函数原型为:#include<pthread.h>int

pthread_equal(pthread_ttid1,pthread_ttid2);返回值:测试两个线程号ID是否相同。若相等返回非0值,否则返回0。函数说明:函数中的参数tid1为线程1的ID,tid2为线程2的ID。7.1.4线程的一次性初始化在传统的顺序编程中,一次性初始化经常通过使用布尔变量来管理。控制变量被静态初始化为0,而任何依赖于初始化的代码都能测试该变量。如果变量值仍然为0,则它能实行初始化,然后将变量置为1。以后检查的代码将跳过初始化。在多线程程序设计中,如果多个线程并发地执行初始化序列代码,2个线程可能发现控制变量为0,并且都实行初始话,而该过程本该仅仅执行一次。初始化的状态必须由互斥量保护。如果需要对一个posix变量静态的初始化,可使用的方法是用一个互斥量对该变量的初始话进行控制。pthread_once函数可以对该变量进行动态初始化。7.1.4线程的一次性初始化如果需要对一个posix变量静态的初始化,可使用的方法是用一个互斥量对该变量的初始化进行控制。pthread_once函数可以对该变量进行动态初始化。其函数原理如下:#include<pthread.h>pthread_once_t

once_control=PTHREAD_ONCE_INIT;int

pthread_once(pthread_once_t*once_control,void(*init_routine)(void));、函数参数:参数once_control为控制变量,init_routine为初始化函数。返回值:若函数调用成功返回0,否则返回错误编号。函数说明:类型为pthread_once_t的变量是一个控制变量。控制变量必须使用PTHREAD_ONCE_INIT宏静态地初始化。例7.4pthread_once函数的应用(once.c)#include<pthread.h>pthread_once_tonce=PTHREAD_ONCE_INIT;pthread_mutex_t

mutex;/*互斥量*/voidonce_init_routine(void)

/*一次初始化函数*/{intstatus;status=pthread_mutex_init(&mutex,NULL);/*初始化互斥量*/

If(status==0)

printf(“Init

success!,Myidis%u”,pthread_self());}void*child_thread(void*arg){printf(“I’mchild,Myidis%u”,pthread_self());

pthread_once(&once,once_init_routine);/*子线程调用一次性初始化函数*/}int

main(int

argc,char*argv[]){pthread_t

child_thread_id;pthread_create(&child_thread_id,NULL,child_thread,NULL);/*创建子线程*/

printf(“I’m

father,myidis%u”,pthread_self());

pthread_once(&once_block,once_init_routine);/*父线程调用一次性初始化函数*/

pthread_join(child_thread_id,NULL);}程序运行结果如下:./onceI’mfather,Myidis3086874304Initsuccess!,Myidis3086874304I’mchild,Myidis3086871472 从上面的结果可以看到当主函数初始化成功后,子函数初始化失败。7.1.5线程的私有数据在进程内的所有线程共享相同的地址空间,任何声明为静态或外部的变量,或在进程堆声明的变量,都可以被进程所有的线程读写。但有时在应用程序设计过程中有必要提供线程私有的全局变量,仅在线程中有效,却可跨多个函数进行访问,如程序可能需要每个线程维护一个链表,要使用相同的函数操作,最简单的办法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由POSIX线程库维护,称为线程私有数据(Thread-SpecificData,TSD)。7.1.5线程的私有数据POSIX线程库提供了函数pthread_key_create来创建线程键。其函数原型为:#include<pthread.h>int

pthread_key_create(pthread_key*key,void(*destructor)(void*));函数参数:参数key为私有数据键,destructor为清理函数。返回值:若函数调用成功返回0,否则返回错误编号。函数说明:函数用于建立线程私有数据键。函数的第1个参数为指向一个键值的指针,第2个参数指明了一个destructor函数(清理函数),如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。这个函数常和函数pthread_once一起使用,为了让这个键只被创建一次。函数pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这个函数,以后的调用将被它忽略。

例7.5pthread_key_create函数的应用#include<pthread.h>pthread_key_t

tsd_key;pthread_once_t

key_once=PTHREAD_ONCE_INIT;voidonce_routine(void){intstatus;status=pthread_key_create(&tsd_key,NULL);/*初始化线程私有数据键*/if(status=0)printf(“Keycreatesuccess!Myidis%u\n”,pthread_self());}void*child_thread(void*arg){printf(“I’m

child,Myidis%u\n”,pthread_self());

pthread_once(&key_once,once_routine);/*调用一次性初始化函数*/}int

main(int

argc,char*argv[]){pthread_t

child_thread_id;pthread_create(&child_thread_id,NULL,child_thread,NULL);printf(“I’m

father,myidis%u\n”,pthread_self());pthread_once(&key_once,once_routine);}程序运行结果如下:I’mfather,Myidis3086231232Keycreatesuccess!Myidis3086231232I’mchild,Myidis20862284007.2信号灯7.2.1

POSIX有名信号灯的API函数有名信号灯总是既可用于线程间的同步,又可以用于进程间的同步。1.sem_open函数sem_open函数用于创建一个新的有名信号灯或打开一个已存在的有名信号灯。函数原型如下:#include<semaphore.h>sem_t*sem_open(constchar*name,int

oflag,/*mode_t

mode,unsigned

intvalue*/);函数参数:参数name是信号灯外部名字,oflag为选择创建或打开一个现有的信号灯,mode是权限位,value是信号灯初值。返回值:若函数调用成功时返回指向信号灯的指针,出错时为SEM_FAILED。函数说明:oflag参数可以是0、O_CREAT(创建一个信号灯)或O_CREAT|O_EXCL(如果没有指定的信号灯就创建),如果指定了O_CREAT,那么第3个和第4个参数是需要的;其中mode参数指定权限位,value参数指定信号灯的初始值,通常用来指定共享资源的页面。该初始不能超过SEM_VALUE_MAX,这个常值必须低于为32767。二值信号灯的初始值通常为1,计数信号灯的初始值则往往大于1。

如果指定了O_CREAT(而没有指定O_EXCL),那么只有所需的信号灯尚未存在时才初始化它。所需信号灯已存在条件下指定O_CREAT不是一个错误。该标志的意思仅仅是“如果所需信号灯尚未存在,那就创建并初始化它”。但是所需信号灯等已存在条件下指定O_CREAT|O_EXCL却是一个错误。

sem_open返回指向sem_t信号灯的指针,该结构里记录着当前共享资源的数目。2.sem_close函数用于关闭有名信号灯。函数原型如下:#include<semaphore.h>int

sem_close(sem_t*sem);函数参数:参数sem是指向信号灯的指针。返回值:若函数调用成功则返回0,否则返回-1。函数说明:一个进程终止时,内核还对其上仍然打开着的所有有名信号灯自动执行这样的信号灯关闭操作。不论该进程是自愿终止的还是非自愿终止的,这种自动关闭都会发生。

但应注意的是关闭一个信号灯并没有将它从系统中删除。即Posix有名信号灯至少是随内核持续的:即使当前没有进程打开着某个信号灯,它的值仍然保持。

3.sem_unlink函数sem_unlink函数用于在所有进程关闭了命名信号量之后,将信号量从系统中删除。函数原型如下:#include<semaphore.h>int

sem_unlink(countchar*name);函数参数:参数name是信号灯的外部名字。返回值:若函数调用成功则返回0,否则返回-1。函数说明:有名信号灯使用sem_unlink从系统中删除。每个信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。

4.sem_getvalue函数函数sem_getvalue用于测试信号灯。函数原型如下:#include<semaphore.h>int

sem_getvalue(sem_t*sem,int*valp);函数参数:参数sem是指向信号灯的指针。返回值:若函数调用成功则返回0,否则返回-1。函数说明:sem_getvalue在由valp指向的正数中返回所指定信号灯的当前值。如果该信号灯当前已上锁,那么返回值或为0,或为某个负数,其绝对值就是等待该信号灯解锁的线程数。5.sem_wait/sem_trywait函数函数用于等待共享资源。函数原型如下:#include<semaphore.h>int

sem_wait(sem_t*sem);int

sem_trywait(sem_t*sem);函数参数:参数sem是指向信号灯的指针。返回值:若函数调用成功则返回0,否则返回-1。函数说明:可以用sem_wait来申请共享资源,sem_wait函数可以测试所指定信号灯的值,如果该值大于0,那就将它减1并立即返回。可以使用申请来的共享资源。如果该值等于0,调用线程就被进入睡眠状态,直到该值变为大于0,这时再将它减1,函数随后返回。sem_wait操作必须是原子的。sem_wait和sem_trywait的差别是:当所指定信号灯的值已经是0时,后者并不将调用线程投入睡眠。相反,它返回一个EAGAIN错误。6.sem_post函数函数sem_post(sem_t*sem)用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,函数原型如下:#include<semaphore.h>int

sem_post(sem_t*sem);int

sem_getvalue(sem_t*sem,int*valp);函数参数:参数sem是指向信号灯的指针。返回值:若函数调用成功则返回0,否则返回-1。函数说明:当一个线程使用完某个信号灯时,它应该调用sem_post来告诉系统申请的资源已经用完。本函数和sem_wait函数功能正好相反,它把所指定的信号灯的值加1,然后唤醒正在等待该信号灯值变为正数的任意线程。posix有名信号灯使用时应注意以下几点:(1)Posix有名信号灯的值是随内核持续的。也就是说,一个进程创建了一个信号灯,这个进程结束后,这个信号灯还存在,并且信号灯的值也不会改变。(2)当持有某个信号灯锁的进程没有释放它就终止时,内核并不给该信号灯解锁。例7.6posix有名信号灯应用于多线程(7-6.c)#include<semaphore.h>#include<unistd.h>#include<stdio.h>#include<fcntl.h>#include<thread.h>void*thread_function(void*arg);/*线程入口函数*/voidprint(pid_t);/*共享资源函数*/sem_t*sem;/*定义Posix有名信号灯*/int

val;/*定义信号灯当前值*/int

main(int

argc,char*argv[]){intn=0;if(argc!=2){printf(“pleaseinputafilename!\n”);exit(1);}sem=sem_open(argv[1],O_CREAT,0644,3);/*打开一个信号灯*/while(n++<5)/*循环创建5个子线程,使它们同步运行*/{if((pthread_create(&a_thread,NULL,

thread_function,NULL))!=0)

{perror(“Threadcreationfailed”);exit(1);}}pthread_join(a_thread,NULL);sem_close(bin_sem);sem_unlink(argv[1]);}void*thread_function(void*arg){sem_wait(sem);/*申请信号灯*/print();/*调用共享代码段*/sleep(1);

sem_post(sem);/*释放信号灯*/

printf(“I’m

finished,my

tidis%d\n”,pthread_self());}voidprint(){printf(“Igetit,my

tidis%d\n”,pthread_self());sem_getvalue(sem,&val);printf(“Nowthevaluehave%d\n”,val);}

程序用循环的方法建立5个线程,然后让它们调用同一个线程处理函数thread_function,在函数里利用信号量来限制访问共享资源的线程数。共享资源用print函数来代表,在真正编程中它有可以是一个终端设备(如打印机)或是一段有实际意义的代码。运行结果为:#gcc–lpthread–o7_6.c#./7_6testIgetit,my

tidis1082330304Nowthevaluehave2Igetit,my

pidis1894Nowthevaluehave1Igetit,my

pidis1895Nowthevaluehave0I’mfinished,my

pidis1893I’mfinished,my

pidis1894I’mfinished,my

pidis1895Igetit,my

pidis1896Nowthevaluehave2Igetit,mypidis1897Nowthevaluehave1I’mfinished,my

pidis1896I’mfinished,my

pidis1897例7.7posix有名信号灯应用于多进程(7-7.c)下面是应用Posix有名信号灯的一个小程序。用它来限制访问共享代码的进程数目。#include<semaphore.h>#include<unistd.h>#include<stdio.h>#include<fcntl.h>voidprint(pid_t);sem_t*sem;/*定义Posix有名信号灯*/int

val;/*定义信号灯当前值*/int

main(int

argc,char*argv[]){intn=0;if(argc!=2){printf(“pleaseinputafilename!\n”);exit(1);}sem=sem_open(argv[1],O_CREAT,0644,2);/*打开一个信号灯,初值设为2*/while(n++<5)/*循环创建5个子进程,使它们同步运行*/{if(fork()==0){sem_wait(sem);/*申请信号灯*/

print(getpid());/*调用共享代码段*/sleep(1);

sem_post(sem);/*释放信号灯*/

printf(“I’m

finished,my

pidis%d\n”,getpid());

return0;}wait();/*等待所有子进程结束*/sem_close(sem);sem_unlink(argv[1]);exit(0);}voidprint(pid_t

pid){printf(“Igetit,my

pidis%d\n”,pid);

sem_getvalue(sem,&val);

printf(“Nowthevaluehave%d\n”,val);}程序编译后运行会得到如下结果:#./7_7testIgetit,my

tidis1082330304Nowthevaluehave1Igetit,my

tidis1090718784Nowthevaluehave0Ifinished,my

pidis1082330304Ifinished,my

pidis1090718784Igetit,my

tidis1099107264Nowthevaluehave1Igetit,my

tidis1116841120Nowthevaluehave0Ifinished,my

pidis1099107264Ifinished,my

pidis1116841120Igetit,my

tidis1125329600Nowthevaluehave1Ifinished,my

pidis1125329600

7.2.2

POSIX基于内存的信号灯的API函数1.sem_init/sem_destroy/sem_getvalue

函数

函数sem_init/sem_destroy/sem_getvalue用于初始化/关闭信号/获取信号灯的值等。函数原型如下:#include<semaphore.h>int

sem_init(sem_t*sem,int

shared,unsigned

intvalue);int

sem_getvalue(sem_t*sem);/*获取信号灯的值*/函数参数:参数sem为指向信号灯的指针,shared是作用范围,value是信号灯初始值。返回值:若函数调用成功则返回0,否则返回-1。函数说明:基于内存的信号灯是由sem_init初始化的。sem参数指向必须由应用程序分配的sem_t变量。如果shared为0,那么待初始化的信号灯是在同一进程的各个线程共享的,否则该信号灯是在进程间共享的。当shared为零时,该信号灯必须存放在即将使用它的所有进程都能访问的某种类型的共享内存中。跟sem_open一样,value参数是该信号灯的初始值。使用完一个基于内存的信号灯后,可调用sem_destroy关闭它。除了sem_open和sem_close外,其它的poisx有名信号灯函数都可以用于基于内存的信号灯。注意:posix基于内存的信号灯和posix有名信号灯的区别,区别如下。(1)sem_open不需类型与shared的参数,有名信号灯总是可在不同进程间共享的。(2)sem_init不使用任何类似于O_CREAT标志的东西,也就是说,sem_init总是初始化信号灯的值。因此,对于一个给定的信号灯,必须小心保证只调用一次sem_init。(3)sem_open返回一个指向某个sem_t变量的指针,该变量由函数本身分配并初始化。但sem_init的第一个参数是一个指向某个sem_t变量的指针,该变量由调用者分配,然后由sem_init函数初始化。(4)posix有名信号灯是通过内核持续的,一个进程创建一个信号灯,另外的进程可以通过该信号灯的外部名(创建信号灯使用的文件名)来访问它。例7.8下面是posix基于内存的信号灯实现一个进程的各个线程间的互次。#include<semaphore.h>#include<unistd.h>#include<stdio.h>#include<fcntl.h>#include<pthread.h>#incude<stdlib.h>void*thread_function(void*arg);/*线程入口函数*/voidprint(void);/*共享资源*/sem_t

bin_sem;/*定义信号灯*/intvalue;/*定义信号量的

intmain(){intn=0;pthread_t

a_thread;if((sem_init(&bin_sem,0,2))!=0)/*初始化信号灯,初始值为2*/{perror(“sem_init”);exit(1);}while(n++<5)/*循环创建5个线程*/{if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0){perror(“Threadcreationfailed”);exit(1);}}pthread_join(a_thread,NULL);/*等待子线程返回*/}void*thread_function(void*arg){sem_wait(&bin_sem);/*等待信号灯*/print();sleep(1);sem_post(&bin_sem);/*挂出信号灯*/printf(“I

finished,my

pidis%d\n”,pthread_self());pthread_exit(arg);}voidprint(){printf(“Igetit,my

tidis%d\n”,pthread_self());sem_getvalue(&bin_sem,&value);/*获取信号灯的值*/printf(“Nowthevaluehave%d\n”,value);}下面是运行结果:#gcc–lpthread–oseminitthread

seminitthread.c#./seminitthread

Igetit,my

tidis1082330304Nowthevaluehave1Igetit,my

tidis1090718784Nowthevaluehave0Ifinished,my

pidis1082330304Ifinished,my

pidis1090718784Igetit,my

tidis1099107264Nowthevaluehave1Igetit,my

tidis1116841120Nowthevaluehave0Ifinished,my

pidis1099107264Ifinished,my

pidis1116841120Igetit,my

tidis1125329600Nowthevaluehave1Ifinished,my

pidis1125329600//下面是用exit()函数结束进程的程序代码:#include<stdlib.h>#include<stdio.h>int

main(void){printf("Usingexit...\n");

printf("Thisisthecontentinbuffer");

exit(0);}输出信息:Usingexit...Thisisthecontentinbuffer7.3互斥量1.mutex线程访问控制(1)初始化互斥量用pthread_mutex_t数据类型来表示,在使用互斥量之前,必须首先对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化,如果动态地分配互斥量,那么释放内存前需要调用pthread_mutex_destroy。下面介绍常用的互斥量函数。pthread_mutex_init函数用于初始化互斥锁。用于初始化互斥锁。其函数#include<pthread.h>int

pthread_mutex_init(pthread_mutex_t*mutex,constpthread_mutex_t*attr);//初始化互斥锁int

pthread_mutex_destroy(pthread_mutex_t*mutex);//销毁互斥锁函数参数:参数mutex是互斥量,attr为互斥锁属性。返回值:若函数调用成功则返回0,否则返回错误编号。。函数说明:mutex为要锁住的互斥量,attr是互斥锁的属性。如果要使用默认的属性初始化互斥量,只需把attr设置为NULL。(2)互斥量加锁函数对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。互斥量加锁函数原型如下:#include<pthread.h>int

pthread_mutex_lock(pthread_mutex_t*mutex);int

pthread_mutex_trylock(pthread_mutex_t*mutex);函数参数:参数mutex是互斥量返回值:成功则返回0,出错则返回错误编号函数说明:trylock函数,这个函数是非阻塞调用模式,也就是说,如果互斥量没被锁住,trylock函数将把互斥量加锁,并获得对共享资源的访问权限;如果互斥量被锁住了,trylock函数将不会阻塞等待而直接返回EBUSY,表示共享资源处于忙状态。(3)互斥量解锁函数函数原型:#include<pthread.h>int

pthread_mutex_unlock(pthread_mutex_t*mutex);函数参数:参数mutex是互斥量返回值:成功则返回0,出错则返回错误编号对互斥量进行加锁,需要调用pthread_mutex_lock,如果互斥量已经上锁,调用线程阻塞直至互斥量解锁。对互斥量解锁,需要调用pthread_mutex_unlock.

如果线程不希望被阻塞,可以使用pthread_mutex_trylock尝试对互斥量进行加锁。如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,否则就会失败,不能锁住互斥量,而返回EBUSY。例

7.9对互斥量加锁#inlcude<stdio.h>#include<pthread.h>#inlcude<stdio.h>#include<unistd.h>void*thread_function(void*arg);int

run_now=1;/*用run_now代表共享资源*/intmain(){intprint_count1=0;/*用于控制循环*/pthread_t

a_thread;if(pthread_create(&a_thread,NULL,thread_function,NULL)!=0)/*创建一个线程*/{

perror(“Thread

createionfailed”);exit(1);}while(print_count1++<5){if(run_now==1)/*主线程:如果run_now为1就把它修改为2*/{printf(“mainthreadisrun\n”);

run_now=2;}else{printf(“mainthreadissleep\n”);sleep(1);}}pthread_join(a_thread,NULL);/*等待子线程结束*/exit(0);}void*thread_function(void*arg){intprint_count2=0;while(print_count2++<5){

if(run_now==2)/子线程:如果run_now为2就把它修改为1*/{printf(“functionthreadisrun\n”);

run_now=1;}else{

printf(“functionthreadissleep\n”);sleep(1);}}pthread_exit(NULL);}运行上面程序的运行结果为:functionthreadissleepmainthreadisrunmainthreadissleepmainthreadissleepfunctionthreadisrunfunctionthreadissleepmainthreadisrunmainthreadissleepfunctionthreadisrunfunctionthreadissleep可以看到main线程和function线程是交替运行的。它们都可以对run_now进行操作。例7.10下面是加锁的程序。#inlcude<stdio.h>#include<pthread.h>#inlcude<stdio.h>viid*thread_function(void*arg);int

run_now=1;/*用run_now代表共享资源*/pthread_mutex_t

work_mutex;/*定义互斥量*/intmain(){intres;intprint_count1=0;prhread_t

a_thread;if(pthread_mutex_init(&work_mutex,NULL)!=0)/*初始化互斥量*/{perror(“Mutexinitfaied”);exit(1);}if(pthread_create(&a_thread,NULL,thread_function,NULL)!=0)/*创建新线程*/{perror(“Thread

createionfailed”);exit(1);}if(pthread_mutex_lock(&work_mutex)!=0)/*对互斥量加锁*/{

preeor(“Lockfailed”);exit(1);}else

printf(“mainlock\n”);while(print_count1++<5){if(run_now==1)/主线程:如果run_now为1就把它修改为2*/{printf(“mainthreadisrun\n”);

run_now=2;}else{printf(“mainthreadissleep\n”);sleep(1);}}if(pthread_mutex_unlock(&work_mutex)!=0)/*对互斥量解锁*/{preeor(“unlockfailed”);exit(1);}else

printf(“mainunlock\n”);pthread_mutex_destroy(&work_mutex);/*收回互斥量资源*/pthread_join(a_thread,NULL);/*等待子线程结束*/exit(0);}void*thread_function(void*arg){intprint_count2=0;sleep(1);if(pthread_mutex_lock(&work_mutex)!=0){perror(“Lockfailed”);exit(1);}else

printf(“functionlock\n”);while(print_count2++<5){if(run_now==2)/分进程:如果run_now为1就把它修改为1*/{printf(“functionthreadisrun\n”);

run_now=1;}else{printf(“functionthreadissleep\n”);sleep(1);}}if(pthread_mutex_unlock(&work_mutex)!=0)/*对互斥量解锁*/{perror(“unlockfailed”);exit(1);}else

printf(“functionunlock\n”);pthread_exit(NULL);}下面是运行结果:mainlockmainthreadisrunmainthreadissleepmainthreadissleepmainthreadissleepmainthreadissleepmainunlockfunctionlockfunctionthreadisrunfunctionthreadissleepfunctionthreadissleepfunctionthreadissleepfunctionthreadissleepfunctionunlock2.互斥锁属性线程和线程的同步对象(互斥量,读写锁,条件变量)都具有属性。在修改属性前都需要对该结构进行初始化。使用后要把该结构回收。用pthread_mutexattr_init函数对pthread_mutexattr结构进行初始化。用pthread_mutexattr_destroy函数对该结构进行回收。(1)pthread_mutexattr_init/pthread_mutexattr_destroy函数用于初始化/回收pthread_mutexattr_t结构。其函数原型如下:#include<pthread.h>int

pthread_mutexattrattr_init(pthread_mutexattr_t*attr);int

pthread_mutexattrattr_destroy(pthread_mutexattr_t*attr);函数参数:pthread_mutexattr_t结构变量,attr为互斥锁属性。返回值:若函数调用成功则返回0,否则返回错误编号。。函数说明:pthread_mutexattr_init将属性对象的值初始化为缺省值。并分配属性对象占用的内存空间。参数attr中pshared属性表示用这个属性对象创建的互斥锁的作用域,它的取值可以是PTHREAD_PROCESS_PRIVATE(缺省值该属性对象创建的互斥锁只能在进程内使用)或PTHREAD_PROCESS_SHARED。(2)pthread_mutexattr_getpshared/pthread_mutexattr_setpshared函数用于获得/修改共享互斥量属性。其函数原型如下:

#include<pthread.h>

int

pthread_mutexattrattr_getpshared(constpthread_attr_t*restrictattr,int*restrictpshared);

int

pthread_mutexattrattr_setpshared(constpthread_attr_t*restrictattr,int

pshared);

返回值:若函数调用成功则返回0,否则返回错误编号。函数说明:互斥量属性分为共享互斥量属性和类型互斥量属性。两种属性分别由不同的函数得到并由不同的函数进行修改。pthread_mutexattr_getpshared/pthread_mutexattr_setpshared函数可以获得和修改共享互斥量属性。pthread_mutexattr_gettype和pthread_mutexattr_settype函数可以获得和修改类型互斥量属性。

共享互斥量属性用于规定互斥锁的作用域。互斥锁的域可以是进程内的也可以是进程间的。pthread_mutexattrattr_getpshared可以返回属性对象的互斥锁作用域属性。可是以下值:PTHREAD_PROCESS_SHARED,PTHREAD_PROCESS_PRIVATE。如果互斥锁属性对象的pshared属性被置PTHREAD_PROCESS_SHARED。那么由这个属性对象创建的互斥锁将被保存在共享内存中,可以被多个进程中的线程共享。如果pshared属性被置为PTHREAD_PROCESS_PRIVATE,那么只有和创建这个互斥锁的线程在同一个进程中的线程才能访问这个互斥锁。

(3)pthread_mutexattr_gettype/pthread_mutexattr_settype

函数用于获得/修改类型互斥量属性。其函数原型如下:

#include<pthread.h>

int

pthread_mutexattr_gettype(pthread_mutexattr_t*attr,int*type);

int

pthread_mutexattr_settype(pthread_mutexattr_t*attr,inttype);返回值:若函数调用成功则返回0,否则返回错误编号。。函数说明:pthread_mutexattr_gettype函数可以获得互斥锁类型属性,pthread_mutexattr_settype函数可用来设置互斥锁的type属性。缺省的互斥锁类型属性是PTHREAD_MUTEX_DEFAULT。合法的类型属性值有:PTHREAD_MUTEX_NORMAL;PTHREAD_MUTEX_ERRORCHECK;PTHREAD_MUTEX_RECURSIVE;PTHREAD_MUTEX_DEFAULT。类型说明:①PTHREAD_MUTEX_NORMAL这种类型的互斥锁不会自动检测死锁。如果一个线程试图对一个互斥锁重复锁定,将会引起这个线程的死锁。如果试图解锁一个由别的线程锁定的互斥锁会引发不可预料的结果。如果一个线程试图解锁已经被解锁的互斥锁也会引发不可预料的结果。②PTHREAD_MUTEX_ERRORCHECK这种类型的互斥锁会自动检测死锁。若一个线程试图对一个互斥锁重复锁定,则返回错误代码。若试图解锁一个由别的线程锁定的互斥锁,则返回错误代码。若一个线程试图解锁已经被解锁的互斥锁,则返回错误代码。③PTHREAD_MUTEX_RECURSIVE一个线程对这种类型的互斥锁重复上锁,不会引起死锁,一个线程对这类互斥锁的多次重复上锁必须由这个线程来重复相同数量的解锁,否则别的线程不能得到这个互斥锁。试图解锁一个由别的线程锁定的互斥锁将会返回错误代码。一个线程试图解锁已经被解锁的互斥锁也将会返回错误代码。这种类型的互斥锁只能是进程私有的(作用域属性为THREAD_PROCESS_PRIVATE)。④PTHREAD_MUTEX_DEFAULT

这种类型的互斥锁不会自动检测死锁。如果一个线程试图对一个互斥锁重复锁定,将会引起不可预料的结果。如果试图解锁一个由别的线程锁定的互斥锁会引发不可预料的结果。如果一个线程试图解锁已经被解锁的互斥锁也会引发不可预料的结果。POSIX标准规定,对于某一具体的实现,可以把这种类型的互斥锁定义为其他类型的互斥锁。应用互斥量需要注意的几点:(1)互斥量需要时间来加锁和解锁。锁住较少互斥量的程序通常运行得更快。所以,互斥量应该尽量少,够用即可,每个互斥量保护的区域应则尽量大。(2)互斥量的本质是串行执行。如果很多线程需要领繁地加锁同一个互斥量,则线程的大部分时间就会在等待,这对性能是有害的。如果互斥量保护的数据(或代码)包含彼此无关的片段,则可以特大的互斥量分解为几个小的互斥量来提高性能。这样,任意时刻需要小互斥量的线程减少,线程等待时间就会减少。所以,互斥量应该足够多(到有意义的地步),每个互斥量保护的区域则应尽量的少。

7.4条件变量与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使“条件成立”(给出条件成立信号)。条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程。1.初始化条件变量属性使用pthread_condattr_init函数可以将与该对象相关联的属性初始化为其缺省值。在执行过程中,线程系统会为每个属性对象分配存储空间。其函数原型如下:#include<pthread.h>int

pthread_condattr_init(pthread_condattr_t*attr);返回值:若函数调用成功则返回0,否则返回错误编号,错误码为ENOMEM,表示分配的内存不足,无法初始化线程属性对象;错误码为EINVAL表示attr

指定的值无效。函数说明:函数调用时,pshared

属性的缺省值为PTHREAD_PROCESS_PRIVATE,表示可以在进程内使用已初始化的条件变量。attr

的数据类型为pthread_condattr_t,其中包含一个由系统分配的属性对象。attr

范围可能的值为PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。2.删除条件变量属性使用pthread_condattr_destroy函数可以删除存储并使属性对象无效。其函数原型如下:#include<pthread.h>int

pthread_condattr_destroy(pthread_condattr_t*attr);返回值:若函数调用成功则返回0,否则返回错误编号。函数说明:条件变量属性必须首先由pthread_condattr_destroy函数删除条件变量属性后,重新初始化后才能重用。3.设置条件变量的范围pthread_condattr_setpshared函数可用来将条件变量的范围设置为进程专用(进程内)或系统范围内(进程间)。其函数原型如下:#include<pthread.h>Int

pthread_condattr_setpshared(pthread_condattr_t*cattr,int

pshared);返回值:若函数调用成功则返回0,否则返回错误编号。函数说明:条件变量属性必须首先由pthread_condattr_destroy函数删除条件变量属性重新初始化后才能重用。例如:ret=pthread_condattr_setpshared(&cattr,PTHREAD_PROCESS_SHARED);或ret=pthread_condattr_setpshared(&cattr,PTHREAD_PROCESS_PRIVATE);如果pshared

属性在共享内存中设置为PTHREAD_PROCESS_SHARED,则其所创建的条件变量可以在多个进程中的线程之间共享。此行为与最初的Solaris线程实现中mutex_init()中的USYNC_PROCESS标志等效。如果互斥锁的pshared

属性设置为PTHREAD_PROCESS_PRIVATE,则仅有那些由同一个进程创建的线程才能够处理该互斥锁。PTHREAD_PROCESS_PRIVATE是缺省值。PTHREAD_PROCESS_PRIVATE所产生的行为与在最初的Solaris线程的cond_init()调用中使用USYNC_THREAD标志相同。PTHREAD_PROCESS_PRIVATE的行为与局部条件变量相同。

4.获取条件变量的范围pthread_condattr_getpshared函数可用来获取属性对象cattr

的pshared

的当前值。其函数原型如下:#include<pthread.h>int

pthread_condattr_getpshared(const

pthread_condattr_t*cattr,in

温馨提示

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

评论

0/150

提交评论