志在指尖
用双手敲打未来

linux多线程编程(实例详解)

linux多线程编程

内存映射mmap。
给每一个线程合理的分配任务。
多线程的完成。
详细的完成代码如下:
/*************************************************************************
>FileName:multithread_copy.c
>Author:lucifer
>Mail:lucifer@163.com
>CreatedTime:2014年11月14日星期五17时43分36秒
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineN5
intnthread=0;
structallocate_task
{
char*start;
char*end;
intsize;
intnum;
};
voidsys_err(constchar*str)
{
perror(str);
exit(-1);
}
void*thread_copy(void*arg)
{
inti;
structallocate_task*s=(structallocate_task*)arg;
for(i=0;i<nthread;i++)
{
memcpy(s[i].end,s[i].start,s[i].size);
}
}
intmain(intargc,char*argv[])
{
intfdsrc,fddest,i,err,total_size,task_size;
structstatsbuf;
char*psrc,*pdest;
if(argc<3)
{
fprintf(stdout,”%ssrcnamedestname\n”,argv[0]);
exit(-1);
}
if(argc=3)
nthread=N;
else
nthread=atoi(argv[3]);
if(stat(argv[1],&sbuf)<0)
sys_err(“stat”);
total_size=sbuf.st_size;
fddest=open(argv[2],O_CREAT|O_RDWR|O_TRUNC,0664);
if(fddest<0)
sys_err(“open”);
if(lseek(fddest,total_size-1,SEEK_SET)<0)
sys_err(“lseek”);
write(fddest,”\0″,1);
fdsrc=open(argv[1],O_RDONLY);
if(fdsrc<0)
sys_err(“open”);
psrc=mmap(NULL,total_size,PROT_READ,MAP_PRIVATE,fdsrc,0);
if(psrc==MAP_FAILED)
sys_err(“mmap”);
pdest=mmap(NULL,total_size,PROT_WRITE,MAP_SHARED,fddest,0);
if(pdest==MAP_FAILED)
sys_err(“mmap”);
close(fdsrc);
close(fddest);
structallocate_task*work=malloc(nthread*sizeof(structallocate_task));
task_size=total_size/nthread;
for(i=0;i<nthread-1;i++)
{
work[i].start=psrc+i*task_size;
work[i].end=pdest+i*task_size;
work[i].size=task_size;
}
work[i].start=psrc+i*task_size;
work[i].end=pdest+i*task_size;
work[i].size=total_size-task_size*(nthread-1);
work[i].num=i;
pthread_ttid[nthread];
for(i=0;i<nthread;i++)
{
err=pthread_create(&tid[i],NULL,thread_copy,(void*)work);
if(err!=0)
{
printf(“%s\n”,strerror(err));
break;
}
}
for(i=0;i<nthread;i++)
{
pthread_join(tid[i],NULL);
}
free(work);
return0;
}

linux

linux多线程编程实例详解

线程(thread)技能早在60年代就被提出,但真实应用多线程到操作体系中去,是在80年代中期,solaris是这方面的佼佼者。传统的Unix也支撑线程的概念,可是在一个进程(process)中只答应有一个线程,这样多线程就意味着多进程。现在,多线程技能已经被许多操作体系所支撑,包含Windows/NT,当然,也包含Linux。
为什么有了进程的概念后,还要再引入线程呢?运用多线程到底有哪些好处?什么的体系应该选用多线程?咱们首先有必要答复这些问题。
运用多线程的理由之一是和进程比较,它是一种十分”节省”的多任务操作办法。咱们知道,在Linux体系下,发动一个新的进程有必要分配给它独立的地址空间,树立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种”昂贵”的多任务工作办法。而运转于一个进程中的多个线程,它们互相之间运用相同的地址空间,同享大部分数据,发动一个线程所花费的空间远远小于发动一个进程所花费的空间,而且,线程间互相切换所需的时刻也远远小于进程间切换所需求的时刻。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在详细的体系上,这个数据或许会有较大的区别。
运用多线程的理由之二是线程间便利的通讯机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能经过通讯的办法进行,这种办法不仅费时,而且很不便利。线程则不然,因为同一进程下的线程之间同享数据空间,所以一个线程的数据能够直接为其它线程所用,这不仅便利,而且便利。当然,数据的同享也带来其他一些问题,有的变量不能同时被两个线程所修正,有的子程序中声明为static的数据更有或许给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需求留意的当地。
除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作办法,当然有以下的优点:
1)提高应用程序响应。这对图形界面的程序特别有含义,当一个操作耗时很长时,整个体系都会等候这个操作,此刻程序不会响应键盘、鼠标、菜单的操作,而运用多线程技能,将耗时长的操作(timeconsuming)置于一个新的线程,能够避免这种尴尬的状况。
2)使多CPU体系更加有效。操作体系会确保当线程数不大于CPU数目时,不同的线程运转于不同的CPU上。
3)改进程序结构。一个既长又杂乱的进程能够考虑分为多个线程,成为几个独立或半独立的运转部分,这样的程序会利于了解和修正。
下面咱们先来尝试编写一个简略的多线程程序。
2简略的多线程编程
Linux体系下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需求运用头文件pthread.h,连接时需求运用库libpthread.a。顺便说一下,Linux下pthread的完结是经过体系调用clone()来完结的。clone()是Linux所特有的体系调用,它的运用办法类似fork,关于clone()的详细状况,有兴趣的读者能够去查看有关文档说明。下面咱们展示一个最简略的多线程程序example1.c。
/*example.c*/
#include
#include
voidthread(void)
{
inti;
for(i=0;i<3;i++)
printf(“Thisisapthread./n”);
}
intmain(void)
{
pthread_tid;
inti,ret;
ret=pthread_create(&id,NULL,(void*)thread,NULL);
if(ret!=0)
{
printf(“Createpthreaderror!/n”);
exit(1);
}
for(i=0;i<3;i++)
printf(“Thisisthemainprocess./n”);
pthread_join(id,NULL);
return(0);
}
咱们编译此程序:
gccexample1.c-lpthread-oexample1
运转example1,咱们得到如下成果:
Thisisthemainprocess.
Thisisapthread.
Thisisthemainprocess.
Thisisthemainprocess.
Thisisapthread.
Thisisapthread.
再次运转,咱们或许得到如下成果:
Thisisapthread.
Thisisthemainprocess.
Thisisapthread.
Thisisthemainprocess.
Thisisapthread.
Thisisthemainprocess.
前后两次成果不相同,这是两个线程抢夺CPU资源的成果。上面的示例中,咱们运用到了两个函数,pthread_create和pthread_join,并声明晰一个pthread_t型的变量。
pthread_t在头文件/usr/include/bits/pthreadtypes.h中界说:
typedefunsignedlongintpthread_t;
它是一个线程的标识符。函数pthread_create用来创立一个线程,它的原型为:
externintpthread_create__P((pthread_t*__thread,__constpthread_attr_t*__attr,
void*(*__start_routine)(void*),void*__arg));
第一个参数为指向线程标识符的指针,第二个参数用来设置线程特点,第三个参数是线程运转函数的开端地址,最终一个参数是运转函数的参数。这儿,咱们的函数thread不需求参数,所以最终一个参数设为空指针。第二个参数咱们也设为空指针,这样将生成默许特点的线程。对线程特点的设定和修正咱们将在下一节阐述。当创立线程成功时,函数回来0,若不为0则说明创立线程失败,常见的错误回来代码为EAGAIN和EINVAL。前者表示体系限制创立新的线程,例如线程数目过多了;后者表示第二个参数代表的线程特点值不合法。创立线程成功后,新创立的线程则运转参数三和参数四确定的函数,原来的线程则继续运转下一行代码。
函数pthread_join用来等候一个线程的完毕。函数原型为:
externintpthread_join__P((pthread_t__th,void**__thread_return));
第一个参数为被等候的线程标识符,第二个参数为一个用户界说的指针,它能够用来存储被等候线程的回来值。这个函数是一个线程堵塞的函数,调用它的函数将一向等候到被等候的线程完毕中止,当函数回来时,被等候线程的资源被收回。一个线程的完毕有两种途径,一种是象咱们上面的比如相同,函数完毕了,调用它的线程也就完毕了;另一种办法是经过函数pthread_exit来完结。它的函数原型为:
externvoidpthread_exit__P((void*__retval))__attribute__((__noreturn__));
唯一的参数是函数的回来代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。最终要说明的是,一个线程不能被多个线程等候,不然第一个接收到信号的线程成功回来,其他调用pthread_join的线程则回来错误代码ESRCH。
在这一节里,咱们编写了一个最简略的线程,并把握了最常用的三个函数pthread_create,pthread_join和pthread_exit。下面,咱们来了解线程的一些常用特点以及怎么设置这些特点。
3修正线程的特点
在上一节的比如里,咱们用pthread_create函数创立了一个线程,在这个线程中,咱们运用了默许参数,即将该函数的第二个参数设为NULL。的确,对大多数程序来说,运用默许特点就够了,但咱们仍是有必要来了解一下线程的有关特点。
特点结构为pthread_attr_t,它相同在头文件/usr/include/pthread.h中界说,喜爱追根问底的人能够自己去查看。特点值不能直接设置,须运用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数有必要在pthread_create函数之前调用。特点目标首要包含是否绑定、是否别离、堆栈地址、堆栈大小、优先级。默许的特点为非绑定、非别离、缺省1M的堆栈、与父进程相同级其他优先级。
关于线程的绑定,牵涉到其他一个概念:轻进程(LWP:LightWeightProcess)。轻进程能够了解为内核线程,它位于用户层和体系层之间。体系对线程资源的分配、对线程的操控是经过轻进程来完结的,一个轻进程能够操控一个或多个线程。默许状况下,发动多少轻进程、哪些轻进程来操控哪些线程是由体系来操控的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的”绑”在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时刻片的调度是面向轻进程的,绑定的线程能够确保在需求的时分它总有一个轻进程可用。经过设置被绑定的轻进程的优先级和调度级能够使得绑定的线程满意诸如实时反应之类的要求。
设置线程绑定状况的函数为pthread_attr_setscope,它有两个参数,第一个是指向特点结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创立了一个绑定的线程。
#include
pthread_attr_tattr;
pthread_ttid;
/*初始化特点值,均设为默许值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM);
pthread_create(&tid,&attr,(void*)my_function,NULL);
线程的别离状况决议一个线程以什么样的办法来终止自己。在上面的比如中,咱们采用了线程的默许特点,即为非别离状况,这种状况下,原有的线程等候创立的线程完毕。只要当pthread_join()函数回来时,创立的线程才算终止,才能开释自己占用的体系资源。而别离线程不是这样子的,它没有被其他的线程所等候,自己运转完毕了,线程也就终止了,立刻开释体系资源。程序员应该根据自己的需求,选择适当的别离状况。设置线程别离状况的函数为pthread_attr_setdetachstate(pthread_attr_t*attr,intdetachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(别离线程)和PTHREAD_CREATE_JOINABLE(非别离线程)。这儿要留意的一点是,假如设置一个线程为别离线程,而这个线程运转又十分快,它很或许在pthread_create函数回来之前就终止了,它终止今后就或许将线程号和体系资源移交给其他的线程运用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种状况能够采纳一定的同步办法,最简略的办法之一是能够在被创立的线程里调用pthread_cond_timewait函数,让这个线程等候一瞬间,留出足够的时刻让函数pthread_create回来。设置一段等候时刻,是在多线程编程里常用的办法。可是留意不要运用诸如wait()之类的函数,它们是使整个进程睡眠,并不能处理线程同步的问题。
其他一个或许常用的特点是线程的优先级,它寄存在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行寄存,一般说来,咱们总是先取优先级,对取得的值修正后再寄存回去。下面便是一段简略的比如。
/*example.c*/
#include
#include
voidthread(void)
{
inti;
for(i=0;i<3;i++)
printf(“Thisisapthread./n”);
}
intmain(void)
{
pthread_tid;
inti,ret;
ret=pthread_create(&id,NULL,(void*)thread,NULL);
if(ret!=0)
{
printf(“Createpthreaderror!/n”);
exit(1);
}
for(i=0;i<3;i++)
printf(“Thisisthemainprocess./n”);
pthread_join(id,NULL);
return(0);
}
4线程的数据处理
和进程比较,线程的最大优点之一是数据的同享性,各个进程同享父进程处沿袭的数据段,能够便利的获得、修正数据。但这也给多线程编程带来了许多问题。咱们有必要当心有多个不同的进程拜访相同的变量。许多函数是不行重入的,即同时不能运转一个函数的多个复制(除非运用不同的数据段)。在函数中声明的静态变量常常带来问题,函数的回来值也会有问题。因为假如回来的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后运用该地址指向的数据时,其他线程或许调用此函数并修正了这一段数据。在进程中同享的变量有必要用关键字volatile来界说,这是为了避免编译器在优化时(如gcc中运用-OX参数)改动它们的运用办法。为了维护变量,咱们有必要运用信号量、互斥等办法来确保咱们对变量的正确运用。下面,咱们就逐渐介绍处理线程数据时的有关常识。
4.1线程数据
在单线程的程序里,有两种根本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD:Thread-SpecificData)。它和全局变量很象,在线程内部,各个函数能够象运用全局变量相同调用它,但它对线程外部的其它线程是不行见的。这种数据的必要性是清楚明晰的。例如咱们常见的变量errno,它回来规范的犯错信息。它显然不能是一个局部变量,简直每个函数都应该能够调用它;但它又不能是一个全局变量,不然在A线程里输出的很或许是B线程的犯错信息。要完结诸如此类的变量,咱们就有必要运用线程数据。咱们为每个线程数据创立一个键,它和这个键相关联,在各个线程里,都运用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表相同的数据内容。
和线程数据相关的函数首要有4个:创立一个键;为一个键指定线程数据;从一个键读取线程数据;删去键。
创立键的函数原型为:
externintpthread_key_create__P((pthread_key_t*__key,
void(*__destr_function)(void*)));
第一个参数为指向一个键值的指针,第二个参数指明晰一个destructor函数,假如这个参数不为空,那么当每个线程完毕时,体系将调用这个函数来开释绑定在这个键上的内存块。这个函数常和函数pthread_once((pthread_once_t*once_control,void(*initroutine)(void)))一起运用,为了让这个键只被创立一次。函数pthread_once声明一个初始化函数,第一次调用pthread_once时它履行这个函数,今后的调用将被它忽略。
在下面的比如中,咱们创立一个键,并将它和某个数据相关联。咱们要界说一个函数createWindow,这个函数界说一个图形窗口(数据类型为Fl_Window*,这是图形界面开发工具FLTK中的数据类型)。因为各个线程都会调用这个函数,所以咱们运用线程数据。
/*example.c*/
#include
#include
voidthread(void)
{
inti;
for(i=0;i<3;i++)
printf(“Thisisapthread./n”);
}
intmain(void)
{
pthread_tid;
inti,ret;
ret=pthread_create(&id,NULL,(void*)thread,NULL);
if(ret!=0)
{
printf(“Createpthreaderror!/n”);
exit(1);
}
for(i=0;i<3;i++)
printf(“Thisisthemainprocess./n”);
pthread_join(id,NULL);
return(0);
}
这样,在不同的线程中调用函数createMyWin,都能够得到在线程内部均可见的窗口变量,这个变量经过函数pthread_getspecific得到。在上面的比如中,咱们已经运用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函数的原型如下:
externintpthread_setspecific__P((pthread_key_t__key,__constvoid*__pointer));
externvoid*pthread_getspecific__P((pthread_key_t__key));
这两个函数的参数含义和运用办法是清楚明晰的。要留意的是,用pthread_setspecific为一个键指定新的线程数据时,有必要自己开释原有的线程数据以收回空间。这个进程函数pthread_key_delete用来删去一个键,这个键占用的内存将被开释,但相同要留意的是,它只开释键占用的内存,并不开释该键关联的线程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中界说的destructor函数。线程数据的开释有必要在开释键之前完结。
4.2互斥锁
互斥锁用来确保一段时刻内只要一个线程在履行一段代码。必要性清楚明晰:假定各个线程向同一个文件次第写入数据,最终得到的成果一定是灾难性的。
咱们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,而且咱们假定一个缓冲区只能保存一条信息。即缓冲区只要两个状况:有信息或没有信息。
voidreader_function(void);
voidwriter_function(void);
charbuffer;
intbuffer_has_item=0;
pthread_mutex_tmutex;
structtimespecdelay;
voidmain(void){
pthread_treader;
/*界说延迟时刻*/
delay.tv_sec=2;
delay.tv_nec=0;
/*用默许特点初始化一个互斥锁目标*/
pthread_mutex_init(&mutex,NULL);
pthread_create(&reader,pthread_attr_default,(void*)&reader_function),NULL);
writer_function();
}
voidwriter_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);
}
}
voidreader_function(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为不公开的数据类型,其间包含一个体系分配的特点目标。函数pthread_mutex_init用来生成一个互斥锁。NULL参数标明运用默许特点。假如需求声明特定特点的互斥锁,须调用函数pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数pthread_mutexattr_settype用来设置互斥锁特点。前一个函数设置特点pshared,它有两个取值,PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步本进程的不同线程。在上面的比如中,咱们运用的是默许特点PTHREAD_PROCESS_PRIVATE。后者用来设置互斥锁类型,可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD_MUTEX_DEFAULT。它们分别界说了不同的上所、解锁机制,一般状况下,选用最终一个默许特点。
pthread_mutex_lock声明开端用互斥锁上锁,尔后的代码直至调用pthread_mutex_unlock中止,均被上锁,即同一时刻只能被一个线程调用履行。当一个线程履行到pthread_mutex_lock处时,假如该锁此刻被另一个线程运用,那此线程被堵塞,即程序将等候到另一个线程开释此互斥锁。在上面的比如中,咱们运用了pthread_delay_np函数,让线程睡眠一段时刻,就是为了避免一个线程始终占据此函数。
上面的比如十分简略,就不再介绍了,需求提出的是在运用互斥锁的进程中很有或许会呈现死锁:两个线程试图同时占用两个资源,并按不同的次第确定相应的互斥锁,例如两个线程都需求确定互斥锁1和互斥锁2,a线程先确定互斥锁1,b线程先确定互斥锁2,这时就呈现了死锁。此刻咱们能够运用函数pthread_mutex_trylock,它是函数pthread_mutex_lock的非堵塞版别,当它发现死锁不行避免时,它会回来相应的信息,程序员能够针对死锁做出相应的处理。其他不同的互斥锁类型对死锁的处理不相同,但最首要的仍是要程序员自己在程序设计留意这一点。
4.3条件变量
前一节中咱们讲述了怎么运用互斥锁来完结线程间数据的同享和通讯,互斥锁一个明显的缺陷是它只要两种状况:确定和非确定。而条件变量经过答应线程堵塞和等候另一个线程发送信号的办法弥补了互斥锁的缺乏,它常和互斥锁一起运用。运用时,条件变量被用来堵塞一个线程,当条件不满意时,线程往往解开相应的互斥锁并等候条件发生变化。一旦其它的某个线程改动了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量堵塞的线程。这些线程将从头确定互斥锁并从头测试条件是否满意。一般说来,条件变量被用来进行线承间的同步。
条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化一个条件变量。它的原型为:
externintpthread_cond_init__P((pthread_cond_t*__cond,__constpthread_condattr_t*__cond_attr));
其间cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条件变量的特点结构,和互斥锁相同咱们能够用它来设置条件变量是进程内可用仍是进程间可用,默许值是PTHREAD_PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程运用。留意初始化条件变量只要未被运用时才能从头初始化或被开释。开释一个条件变量的函数为pthread_cond_destroy(pthread_cond_tcond)。
函数pthread_cond_wait()使线程堵塞在一个条件变量上。它的函数原型为:
externintpthread_cond_wait__P((pthread_cond_t*__cond,
pthread_mutex_t*__mutex));
线程解开mutex指向的锁并被条件变量cond堵塞。线程能够被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒,可是要留意的是,条件变量仅仅起堵塞和唤醒线程的作用,详细的判别条件还需用户给出,例如一个变量是否为0等等,这一点咱们从后面的比如中能够看到。线程被唤醒后,它将从头查看判别条件是否满意,假如还不满意,一般说来线程应该仍堵塞在这儿,被等候被下一次唤醒。这个进程一般用while语句完结。
另一个用来堵塞线程的函数是pthread_cond_timedwait(),它的原型为:
externintpthread_cond_timedwait__P((pthread_cond_t*__cond,
pthread_mutex_t*__mutex,__conststructtimespec*__abstime));
它比函数pthread_cond_wait()多了一个时刻参数,阅历abstime段时刻后,即便条件变量不满意,堵塞也被免除。
函数pthread_cond_signal()的原型为:
externintpthread_cond_signal__P((pthread_cond_t*__cond));
它用来开释被堵塞在条件变量cond上的一个线程。多个线程堵塞在此条件变量上时,哪一个线程被唤醒是由线程的调度战略所决议的。要留意的是,有必要用维护条件变量的互斥锁来维护这个函数,不然条件满意信号又或许在测试条件和调用pthread_cond_wait函数之间被宣布,然后造成无限制的等候。下面是运用函数pthread_cond_wait()和函数pthread_cond_signal()的一个简略的比如。
pthread_mutex_tcount_lock;
pthread_cond_tcount_nonzero;
unsignedcount;
decrement_count(){
pthread_mutex_lock(&count_lock);
while(count==0)
pthread_cond_wait(&count_nonzero,&count_lock);
count=count-1;
pthread_mutex_unlock(&count_lock);
}
increment_count(){
pthread_mutex_lock(&count_lock);
if(count==0)
pthread_cond_signal(&count_nonzero);
count=count+1;
pthread_mutex_unlock(&count_lock);
}
count值为0时,decrement函数在pthread_cond_wait处被堵塞,并翻开互斥锁count_lock。此刻,当调用到函数increment_count时,pthread_cond_signal()函数改动条件变量,奉告decrement_count()中止堵塞。读者能够试着让两个线程分别运转这两个函数,看看会呈现什么样的成果。
函数pthread_cond_broadcast(pthread_cond_t*cond)用来唤醒所有被堵塞在条件变量cond上的线程。这些线程被唤醒后将再次竞赛相应的互斥锁,所以有必要小心运用这个函数。
4.4信号量
信号量本质上是一个非负的整数计数器,它被用来操控对公共资源的拜访。当公共资源添加时,调用函数sem_post()添加信号量。只要当信号量值大于0时,才能运用公共资源,运用后,函数sem_wait()削减信号量。函数sem_trywait()和函数pthread_mutex_trylock()起相同的作用,它是函数sem_wait()的非堵塞版别。下面咱们逐个介绍和信号量有关的一些函数,它们都在头文件/usr/include/semaphore.h中界说。
信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:
externintsem_init__P((sem_t*__sem,int__pshared,unsignedint__value));
sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间同享,不然只能为当时进程的所有线程同享;value给出了信号量的初始值。
函数sem_post(sem_t*sem)用来添加信号量的值。当有线程堵塞在这个信号量上时,调用这个函数会使其间的一个线程不在堵塞,选择机制相同是由线程的调度战略决议的。
函数sem_wait(sem_t*sem)被用来堵塞当时线程直到信号量sem的值大于0,免除堵塞后将sem的值减一,标明公共资源经运用后削减。函数sem_trywait(sem_t*sem)是函数sem_wait()的非堵塞版别,它直接将信号量sem的值减一。
函数sem_destroy(sem_t*sem)用来开释信号量sem。
/*Filesem.c*/
#include
#include
#include
#defineMAXSTACK100
intstack[MAXSTACK][2];
intsize=0;
sem_tsem;
/*从文件1.dat读取数据,每读一次,信号量加一*/
voidReadData1(void){
FILE*fp=fopen(“1.dat”,”r”);
while(!feof(fp)){
fscanf(fp,”%d%d”,&stack[size][0],&stack[size][1]);
sem_post(&sem);
++size;
}
fclose(fp);
}
/*从文件2.dat读取数据*/
voidReadData2(void){
FILE*fp=fopen(“2.dat”,”r”);
while(!feof(fp)){
fscanf(fp,”%d%d”,&stack[size][0],&stack[size][1]);
sem_post(&sem);
++size;
}
fclose(fp);
}
/*堵塞等候缓冲区有数据,读取数据后,开释空间,继续等候*/
voidHandleData1(void){
while(1){
sem_wait(&sem);
printf(“Plus:%d+%d=%d/n”,stack[size][0],stack[size][1],
stack[size][0]+stack[size][1]);
–size;
}
}
voidHandleData2(void){
while(1){
sem_wait(&sem);
printf(“Multiply:%d*%d=%d/n”,stack[size][0],stack[size][1],
stack[size][0]*stack[size][1]);
–size;
}
}
intmain(void){
pthread_tt1,t2,t3,t4;
sem_init(&sem,0,0);
pthread_create(&t1,NULL,(void*)HandleData1,NULL);
pthread_create(&t2,NULL,(void*)HandleData2,NULL);
pthread_create(&t3,NULL,(void*)ReadData1,NULL);
pthread_create(&t4,NULL,(void*)ReadData2,NULL);
/*避免程序过早退出,让它在此无限期等候*/
pthread_join(t1,NULL);
}
在Linux下,咱们用命令gcc-lpthreadsem.c-osem生成可履行文件sem。咱们事先编辑好数据文件1.dat和2.dat,假定它们的内容分别为12345678910和-1-2-3-4-5-6-7-8-9-10,咱们运转sem,得到如下的成果:
Multiply:-1*-2=2
Plus:-1+-2=-3
Multiply:9*10=90
Plus:-9+-10=-19
Multiply:-7*-8=56
Plus:-5+-6=-11
Multiply:-3*-4=12
Plus:9+10=19
Plus:7+8=15
Plus:5+6=11
从中咱们能够看出各个线程间的竞赛关系。而数值并未按咱们原先的次第显示出来这是因为size这个数值被各个线程任意修正的原因。这也往往是多线程编程要留意的问题。

未经允许不得转载:IT技术网站 » linux多线程编程(实例详解)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载