Linux操作系统下的多线程编程详细解析.docx
《Linux操作系统下的多线程编程详细解析.docx》由会员分享,可在线阅读,更多相关《Linux操作系统下的多线程编程详细解析.docx(17页珍藏版)》请在冰豆网上搜索。
Linux操作系统下的多线程编程详细解析
Linux操作系统下的多线程编程详细解析
(1)
线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。
传统的Unix也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多线程就意味着多进程。
现在,多线程技术已经被许多操作系统所支持,包括Windows/NT,当然,也包括Linux。
为什么有了进程的概念后,还要再引入线程呢?
使用多线程到底有哪些好处?
什么的系统应该选用多线程?
我们首先必须回答这些问题。
使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。
我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。
而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。
据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。
使用多线程的理由之二是线程间方便的通信机制。
对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。
线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:
1)提高应用程序响应。
这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(timeconsuming)置于一个新的线程,可以避免这种尴尬的情况。
2)使多CPU系统更加有效。
操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
3)改善程序结构。
一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
下面我们先来尝试编写一个简单的多线程程序。
简单的多线程编程
Linux系统下的多线程遵循POSIX线程接口,称为pthread。
编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。
顺便说一下,Linux下pthread的实现是通过系统调用clone()来实现的。
clone()是Linux所特有的系统调用,它的使用方式类似fork,关于clone()的详细情况,有兴趣的读者可以去查看有关文档说明。
下面我们展示一个最简单的多线程程序pthread_create.c。
一个重要的线程创建函数原型:
#include
intpthread_create(pthread_t*restricttidp,constpthread_attr_t*restrictattr,void*(*start_rtn)(void),void*restrictarg);
返回值:
若是成功建立线程返回0,否则返回错误的编号
形式参数:
pthread_t*restricttidp要创建的线程的线程id指针
constpthread_attr_t*restrictattr创建线程时的线程属性
void*(start_rtn)(void)返回值是void类型的指针函数
void*restrictarg start_rtn的行参
例程1:
功能:
创建一个简单的线程
程序名称:
pthread_create.c
/********************************************************************************************
** Name:
pthread_create.c
** UsedtostudythemultithreadprogramminginLinuxOS
** Author:
zeickey
** Date:
2006/9/16
** Copyright(c)2006,AllRightsReserved!
*********************************************************************************************/
#include
#include
void*myThread1(void)
{
inti;
for(i=0;i<100;i++)
{
printf("Thisisthe1stpthread,createdbyzieckey.\n");
sleep
(1);//Letthisthreadtosleep1second,andthencontinuetorun
}
}
void*myThread2(void)
{
inti;
for(i=0;i<100;i++)
{
printf("Thisisthe2stpthread,createdbyzieckey.\n");
sleep
(1);
}
}
intmain()
{
inti=0,ret=0;
pthread_tid1,id2;
ret=pthread_create(&id2,NULL,(void*)myThread1,NULL);
if(ret)
{
printf("Createpthreaderror!
\n");
return1;
}
ret=pthread_create(&id2,NULL,(void*)myThread2,NULL);
if(ret)
{
printf("Createpthreaderror!
\n");
return1;
}
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return0;
}
我们编译此程序:
#gccpthread_create.c-lpthread
因为pthread的库不是linux系统的库,所以在进行编译的时候要加上-lpthread,否则编译不过,会出现下面错误
thread_test.c:
在函数‘create’中:
thread_test.c:
7:
警告:
在有返回值的函数中,程序流程到达函数尾
/tmp/ccOBJmuD.o:
Infunction`main':
thread_test.c:
(.text+0x4f):
对‘pthread_create’未定义的引用
collect2:
ld返回1
运行,我们得到如下结果:
#./a.out
Thisisthe1stpthread,createdbyzieckey.
Thisisthe2stpthread,createdbyzieckey.
Thisisthe1stpthread,createdbyzieckey.
Thisisthe2stpthread,createdbyzieckey.
Thisisthe2stpthread,createdbyzieckey.
Thisisthe1stpthread,createdbyzieckey.
....
两个线程交替执行。
此例子介绍了创建线程的方法。
下面例子介绍向线程传递参数。
例程2:
功能:
向新的线程传递整形值
程序名称:
pthread_int.c
/********************************************************************************************
** Name:
pthread_int.c
** UsedtostudythemultithreadprogramminginLinuxOS
** Passaparametertothethread.
** Author:
zeickey
** Date:
2006/9/16
** Copyright(c)2006,AllRightsReserved!
*********************************************************************************************/
#include
#include
#include
void*create(void*arg)
{
int*num;
num=(int*)arg;
printf("createparameteris%d\n",*num);
return(void*)0;
}
intmain(intargc,char*argv[])
{
pthread_ttidp;
interror;
inttest=4;
int*attr=&test;
error=pthread_create(&tidp,NULL,create,(void*)attr);
if(error)
{
printf("pthread_createiscreatedisnotcreated...\n");
return-1;
}
sleep
(1);
printf("pthread_createiscreated...\n");
return0;
}
编译方法:
gcc-lpthreadpthread_int.c-Wall
执行结果:
createparameteris4
pthread_createiscreatedis created...
例程总结:
可以看出来,我们在main函数中传递的整行指针,传递到我们新建的线程函数中。
在上面的例子可以看出来我们向新的线程传入了另一个线程的int数据,线程之间还可以传递字符串或是更复杂的数据结构。
例程3:
程序功能:
向新建的线程传递字符串
程序名称:
pthread_string.c
/********************************************************************************************
** Name:
pthread_string.c
** UsedtostudythemultithreadprogramminginLinuxOS
** Passa‘char*‘parametertothethread.
** Author:
zeickey
** Date:
2006/9/16
** Copyright(c)2006,AllRightsReserved!
*********************************************************************************************/
#include
#include
#include
void*create(void*arg)
{
char*name;
name=(char*)arg;
printf("Theparameterpassedfrommainfunctionis%s \n",name);
return(void*)0;
}
intmain(intargc,char*argv[])
{
char*a="zieckey";
interror;
pthread_ttidp;
error=pthread_create(&tidp,NULL,create,(void*)a);
if(error!
=0)
{
printf("pthreadisnotcreated.\n");
return-1;
}
sleep
(1);
printf("pthreadiscreated...\n");
return0;
}
编译方法:
gcc-Wallpthread_string.c-lpthread
执行结果:
Theparameterpassedfrommainfunctioniszieckey
pthreadiscreated...
例程总结:
可以看出来main函数中的字符串传入了新建的线程中。
例程4:
程序功能:
向新建的线程传递字符串
程序名称:
pthread_struct.c
/********************************************************************************************
** Name:
pthread_struct.c
** UsedtostudythemultithreadprogramminginLinuxOS
** Passa‘char*‘parametertothethread.
** Author:
zeickey
** Date:
2006/9/16
** Copyright(c)2006,AllRightsReserved!
*********************************************************************************************/
#include
#include
#include
#include
structmenber
{
inta;
char*s;
};
void*create(void*arg)
{
structmenber*temp;
temp=(structmenber*)arg;
printf("menber->a=%d \n",temp->a);
printf("menber->s=%s \n",temp->s);
return(void*)0;
}
intmain(intargc,char*argv[])
{
pthread_ttidp;
interror;
structmenber*b;
b=(structmenber*)malloc(sizeof(structmenber));
b->a=4;
b->s="zieckey";
error=pthread_create(&tidp,NULL,create,(void*)b);
if(error)
{
printf("phreadisnotcreated...\n");
return-1;
}
sleep
(1);
printf("pthreadiscreated...\n");
return0;
}
编译方法:
gcc-Wallpthread_struct.c-lpthread
执行结果:
menber->a=4
menber->s=zieckey
pthreadiscreated...
例程总结:
可以看出来main函数中的一个结构体传入了新建的线程中。
线程包含了标识进程内执行环境必须的信息。
他集成了进程中的所有信息都是对线程进行共享的,包括文本程序、程序的全局内存和堆内存、栈以及文件描述符。
例程5:
程序目的:
验证新建立的线程可以共享进程中的数据
程序名称:
pthread_share.c
/********************************************************************************************
** Name:
pthread_share_data.c
** UsedtostudythemultithreadprogramminginLinuxOS
** Passa‘char*‘parametertothethread.
** Author:
zeickey
** Date:
2006/9/16
** Copyright(c)2006,AllRightsReserved!
*********************************************************************************************/
#include
#include
#include
staticinta=4;
void*create(void*arg)
{
printf("newpthread...\n");
printf("a=%d \n",a);
return(void*)0;
}
intmain(intargc,char*argv[])
{
pthread_ttidp;
interror;
a=5;
error=pthread_create(&tidp,NULL,create,NULL);
if(error!
=0)
{
printf("newthreadisnotcreate...\n");
return-1;
}
sleep
(1);
printf("newthreadiscreated...\n");
return0;
}
编译方法:
gcc-Wallpthread_share_data.c-lpthread
执行结果:
newpthread...
a=5
newthreadiscreated...
例程总结:
可以看出来,我们在主线程更改了我们的全局变量a的值的时候,我们新建立的线程则打印出来了改变的值,可以看出可以访问线程所在进程中的数据信息。
Linux操作系统下的多线程编程详细解析
(2)
2、线程的终止
如果进程中任何一个线程中调用exit,_Exit,或者是_exit,那么整个进程就会终止,
与此类似,如果信号的默认的动作是终止进程,那么,把该信号发送到线程会终止进程。
线程的正常退出的方式:
(1)线程只是从启动例程中返回,返回值是线程中的退出码
(2)线程可以被另一个进程进行终止
(3)线程自己调用pthread_exit函数
两个重要的函数原型:
#include
voidpthread_exit(void*rval_ptr);
/*rval_ptr线程退出返回的指针*/
intpthread_join(pthread_tthread,void**rval_ptr);
/*成功结束进程为0,否则为错误编码*/
例程6
程序目的:
线程正常退出,接受线程退出的返回码
程序名称:
pthread_exit.c
/********************************************************************************************
** Name:
pthread_exit.c
** UsedtostudythemultithreadprogramminginLinuxOS
** Aexampleshowingathreadtoexitandwithareturncode.
** Author:
zeickey
** Date: