linux 多线程编程.docx
- 文档编号:7265639
- 上传时间:2023-01-22
- 格式:DOCX
- 页数:15
- 大小:28.12KB
linux 多线程编程.docx
《linux 多线程编程.docx》由会员分享,可在线阅读,更多相关《linux 多线程编程.docx(15页珍藏版)》请在冰豆网上搜索。
linux多线程编程
C语言中,信号量的数据类型为结构sem_t,它本质上是一个长整型的数。
thread1.c
#include
#include
#include
void*thread_function(void*arg){
inti;
for(i=0;i<20;i++){
printf("Threadsayshi!
\n");
sleep
(1);
}
returnNULL;
}
intmain(void){
pthread_tmythread;
if(pthread_create(&mythread,NULL,thread_function,NULL)){
printf("errorcreatingthread.");
abort();
}
if(pthread_join(mythread,NULL)){
printf("errorjoiningthread.");
abort();
}
exit(0);
}
要编译这个程序,只需先将程序存为thread1.c,然后输入:
$gccthread1.c-othread1-lpthread
运行则输入:
$./thread1
理解thread1.c
thread1.c是一个非常简单的线程程序。
虽然它没有实现什么有用的功能,但可以帮助理解线程的运行机制。
下面,我们一步一步地了解这个程序是干什么的。
main()中声明了变量mythread,类型是pthread_t。
pthread_t类型在pthread.h中定义,通常称为“线程id”(缩写为"tid")。
可以认为它是一种线程句柄。
mythread声明后(记住mythread只是一个"tid",或是将要创建的线程的句柄),调用pthread_create函数创建一个真实活动的线程。
不要因为pthread_create()在"if"语句内而受其迷惑。
由于pthread_create()执行成功时返回零而失败时则返回非零值,将pthread_create()函数调用放在if()语句中只是为了方便地检测失败的调用。
让我们查看一下pthread_create参数。
第一个参数&mythread是指向mythread的指针。
第二个参数当前为NULL,可用来定义线程的某些属性。
由于缺省的线程属性是适用的,只需将该参数设为NULL。
第三个参数是新线程启动时调用的函数名。
本例中,函数名为thread_function()。
当thread_function()返回时,新线程将终止。
本例中,线程函数没有实现大的功能。
它仅将"Threadsayshi!
"输出20次然后退出。
注意thread_function()接受void*作为参数,同时返回值的类型也是void*。
这表明可以用void*向新线程传递任意类型的数据,新线程完成时也可返回任意类型的数据。
那如何向线程传递一个任意参数?
很简单。
只要利用pthread_create()中的第四个参数。
本例中,因为没有必要将任何数据传给微不足道的thread_function(),所以将第四个参数设为NULL。
您也许已推测到,在pthread_create()成功返回之后,程序将包含两个线程。
等一等, 两个 线程?
我们不是只创建了一个线程吗?
不错,我们只创建了一个进程。
但是主程序同样也是一个线程。
可以这样理解:
如果编写的程序根本没有使用POSIX线程,则该程序是单线程的(这个单线程称为“主”线程)。
创建一个新线程之后程序总共就有两个线程了。
我想此时您至少有两个重要问题。
第一个问题,新线程创建之后主线程如何运行。
答案,主线程按顺序继续执行下一行程序(本例中执行"if(pthread_join(...))")。
第二个问题,新线程结束时如何处理。
答案,新线程先停止,然后作为其清理过程的一部分,等待与另一个线程合并或“连接”。
现在,来看一下pthread_join()。
正如pthread_create()将一个线程拆分为两个,pthread_join()将两个线程合并为一个线程。
pthread_join()的第一个参数是tidmythread。
第二个参数是指向void指针的指针。
如果void指针不为NULL,pthread_join将线程的void*返回值放置在指定的位置上。
由于我们不必理会thread_function()的返回值,所以将其设为NULL.
您会注意到thread_function()花了20秒才完成。
在thread_function()结束很久之前,主线程就已经调用了pthread_join()。
如果发生这种情况,主线程将中断(转向睡眠)然后等待thread_function()完成。
当thread_function()完成后,pthread_join()将返回。
这时程序又只有一个主线程。
当程序退出时,所有新线程已经使用pthread_join()合并了。
这就是应该如何处理在程序中创建的每个新线程的过程。
如果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。
这意味着如果未对线程做正确的清理,最终会导致pthread_create()调用失败。
thread2.c的代码如下:
thread2.c
#include
#include
#include
#include
intmyglobal;
void*thread_function(void*arg){
inti,j;
for(i=0;i<20;i++){
j=myglobal;
j=j+1;
printf(".");
fflush(stdout);
sleep
(1);
myglobal=j;
}
returnNULL;
}
intmain(void){
pthread_tmythread;
inti;
if(pthread_create(&mythread,NULL,thread_function,NULL)){
printf("errorcreatingthread.");
abort();
}
for(i=0;i<20;i++){
myglobal=myglobal+1;
printf("o");
fflush(stdout);
sleep
(1);
}
if(pthread_join(mythread,NULL)){
printf("errorjoiningthread.");
abort();
}
printf("\nmyglobalequals%d\n",myglobal);
exit(0);
}
理解thread2.c
如同第一个程序,这个程序创建一个新线程。
主线程和新线程都将全局变量myglobal加一20次。
但是程序本身产生了某些意想不到的结果。
编译代码请输入:
$gccthread2.c-othread2-lpthread
运行请输入:
$./thread2
输出:
$./thread2
..o.o.o.o.oo.o.o.o.o.o.o.o.o.o..o.o.o.o.o
myglobalequals21
非常意外吧!
因为myglobal从零开始,主线程和新线程各自对其进行了20次加一,程序结束时myglobal值应当等于40。
由于myglobal输出结果为21,这其中肯定有问题。
首先查看函数thread_function()。
注意如何将myglobal复制到局部变量"j"了吗?
接着将j加一,再睡眠一秒,然后到这时才将新的j值复制到myglobal?
这就是关键所在。
设想一下,如果主线程就在新线程将myglobal值复制给j 后 立即将myglobal加一,会发生什么?
当thread_function()将j的值写回myglobal时,就覆盖了主线程所做的修改。
thread3.c
#include
#include
#include
#include
ntmyglobal;
pthread_mutex_tmymutex=PTHREAD_MUTEX_INITIALIZER;
void*thread_function(void*arg)
{
inti,j;
for(i=0;i<20;i++)
{
pthread_mutex_lock(&mymutex);
j=myglobal;j=j+1;
printf(".");
fflush(stdout);
sleep
(1);
myglobal=j;
pthread_mutex_unlock(&mymutex);
}
returnNULL;
}
intmain(void)
{
pthread_tmythread;
inti;
if(pthread_create(&mythread,NULL,thread_function,NULL))
{
printf("errorcreatingthread.");
abort();
}
for(i=0;i<20;i++)
{
pthread_mutex_lock(&mymutex);
myglobal=myglobal+1;
pthread_mutex_unlock(&mymutex);
printf("o");
fflush(stdout);
sleep
(1);
}
if(pthread_join(mythread,NULL))
{
printf("errorjoiningthread.");
abort();
}
printf("\nmyglobalequals%d\n",myglobal);
exit(0);
}
输出:
o....................ooooooooooooooooooo
myglobalequals40
pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。
pthread_cond_wait() 必须与pthread_mutex配套使用。
pthread_cond_wait()函数一进入wait状态就会自动releasemutex。
当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。
pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
pthread_cond_signal只能唤醒已经处于pthread_cond_wait的线程
也就是说,如果signal的时候没有线程在conditionwait,那么本次signal就没有效果,后续的线程进入conditionwait之后,无法被之前的signal唤醒。
使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。
假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。
如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。
但无论如何一个pthread_cond_signal调用最多发信一次。
但是pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续wait,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程.
另外,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐对pthread_cond_wait() 使用while循环来做条件判断.
图4.采用Linux条件变量模型的出租车实例流程
通过对比结果,你会发现同样的逻辑,在Linux平台上运行的结果却完全是两样。
对于在Windows平台上的模型一,Jack开着出租车到了站台,触发条件变量。
如果没顾客,条件变量将维持触发状态,也就是说Jack停下车在那里等着。
直到Susan小姐来了站台,执行等待条件来找出租车。
Susan搭上Jack的出租车离开,同时条件变量被自动复位。
但是到了Linux平台,问题就来了,Jack到了站台一看没人,触发的条件变量被直接复位,于是Jack排在等待队列里面。
来迟一秒的Susan小姐到了站台却看不到在那里等待的Jack,只能等待,直到Mike开车赶到,重新触发条件变量,Susan才上了Mike的车。
这对于在排队系统前面的Jack是不公平的,而问题症结是在于Linux平台上条件变量触发的自动复位引起的一个Bug。
条件变量在Linux平台上的这种模型很难说好坏。
但是在实际开发中,我们可以对代码稍加改进就可以避免这种差异的发生。
由于这种差异只发生在触发没有被线程等待在条件变量的时刻,因此我们只需要掌握好触发的时机即可。
最简单的做法是增加一个计数器记录等待线程的个数,在决定触发条件变量前检查下该变量即可。
改进后Linux函数如清单5所示。
清单5.Linux出租车案例代码实例
……
//提示出租车到达的条件变量
pthread_cond_ttaxiCond;
//同步锁
pthread_mutex_ttaxiMutex;
//旅客人数,初始为0
inttravelerCount=0;
//旅客到达等待出租车
void*traveler_arrive(void*name){
cout<<”Traveler:
”<<(char*)name<<”needsataxinow!
”< pthread_mutex_lock(&taxiMutex); //提示旅客人数增加 travelerCount++; pthread_cond_wait(&taxiCond,&taxiMutex); pthread_mutex_unlock(&taxiMutex); cout<<”Traveler: ”<<(char*)name<<”nowgotataxi! ”< pthread_exit((void*)0); } //出租车到达 void*taxi_arrive(void*name) { cout<<”Taxi”<<(char*)name<<”arrives.”< while(true) { pthread_mutex_lock(&taxiMutex); //当发现已经有旅客在等待时,才触发条件变量 if(travelerCount>0) { pthread_cond_signal(&taxtCond); pthread_mutex_unlock(&taxiMutex); break; } pthread_mutex_unlock(&taxiMutex); } pthread_exit((void*)0); } #include #include #include #include #include #include #include #include #include #include #include void*testThreadPool(void*t); pthread_mutex_tclifd_mutex=PTHREAD_MUTEX_INITIALIZER; pthread_cond_tclifd_cond=PTHREAD_COND_INITIALIZER; intmain() { char b=9; pthread_t mythread; if(pthread_create(&mythread,NULL,(void*)testThreadPool,(void*)(&b))) { printf("pthread_create\n"); } sleep (2); printf("father: 1\n"); pthread_mutex_lock(&clifd_mutex); sleep (2); printf("father: 2\n"); pthread_cond_signal(&clifd_cond); sleep (2); printf("father: 3\n"); pthread_mutex_unlock(&clifd_mutex); printf("father: 4\n"); } void*testThreadPool(void*t) { printf("child: tis%d\n",(*(char*)t)); printf("child: 1\n"); pthread_mutex_lock(&clifd_mutex); printf("child: 2\n"); pthread_cond_wait(&clifd_cond,&clifd_mutex); printf("child: 3\n"); pthread_mutex_unlock(&clifd_mutex); printf("child: 4\n"); printf("child: 5\n"); } [root@songpthread_cont_wait]#./song child: tis9 child: 1 child: 2 father: 1 father: 2 father: 3 child: 3 child: 4 child: 5 father: 4 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include void*testThreadPool(void*t); void*song(void*s); pthread_mutex_tclifd_mutex=PTHREAD_MUTEX_INITIALIZER; pthread_cond_tclifd_cond=PTHREAD_COND_INITIALIZER; intmain() { char b=9; pthread_t mythread; pthread_t ssthread; if(pthread_create(&mythread,NULL,(void*)testThreadPool,(void*)(&b))) { printf("pthread_create\n"); } if(pthread_create(&ssthread,NULL,(void*)song,(void*)(&b))) { printf("pthread_create\n"); } sleep (2); printf("father: 1\n"); pthread_mutex_lock(&clifd_mutex); sleep (2); printf("father: 2\n"); pthread_cond_signal(&clifd_cond); sleep (2); printf("father: 3\n");
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 多线程编程 多线程 编程