Linux系统编程:线程控制
一、提出问题
问1.线程存在的意义是什么?什么时候适合使用多线程?
答1.在单进程环境中实现多任务,线程可访问其所在进程的资源,例如内存、描述符等。对于单进程,如果要完成多项任务,这些任务只能依次执行,使用多线程可以让多个任务并行执行。
问2.线程的同步和异步有什么不同?同步实现机制分别有哪些?
答2.同步,顾名思义:相同的步调,就比如我们两条腿走路,左右脚之间互相协调,有节奏进行前进。在线程这里同步的意思就是,几个线程为了完成某个任务配合地、互相制约地执行。异步的概念同样可以理解,线程之间的执行没有依赖关系。同步机制适合在。同步的实现机制:互斥量、信号量、读写锁、条件变量。
二、常用函数
①创建线程
#include<pthread.h> int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);
参数分析:
tidp:指向保存线程ID的变量
pthread_attr_t:设置线程属性,通常为NULL
start_rtn:该线程要运行的函数的起始地址。
arg:运行函数的参数。
返回值:成功--0,失败--出错编号
②退出线程:线程主动调用,属于正常退出的一种。
#include <pthread.h> void pthread_exit(void *retval);
retval:指向即将退出的线程所执行的函数的返回值。这里不解的是为什么线程执行的程序的返回值要通过指针参数来返回?先放一下这个问题,或许后面会有答案。
③线程等待:让父进程等待指定的线程,直到该线程结束返回。
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);
参数分析:
thread:要等待的线程ID
retval:指向要等待退出的线程的返回值,这里值得注意的是它是指针的指针。联系上边的内容,pthread_create()函数中指定的线程执行函数start_rtn()是个指针函数——返回值为指针,也就是说在函数执行到最后需要来一句类似于“return 指针”这样的语句,线程调用pthread_exit就意味着“结束返回”,也需要返回这么一个指针,这个指针就是通过pthread_exit函数的参数来设置,如此上边留下的问题就有答案了。同时,问题也来了:start_rtn()指针函数返回的指针值由谁来接受?线程结束时进程在做什么,可能执行其他线程,可能进程在等待某个线程的凯旋归来等等,嗯,如果线程刚好在等待这个线程,那么自然他可以获得线程结束时的返回值——指针,我们知道存放指针的变量叫指针的指针,所以就有了参数“void **retval”。
返回值:成功--0, 失败--错误号.
④获取线程ID:在线程中调用,获取自身线程的ID.
#include <pthread.h> pthread_t pthread_self(void);
⑤线程资源释放:根据需要这是线程在退出之前要进行的操作。
#include <pthread.h> void pthread_cleanup_push(void (*routine)(void*), void *arg);
参数:routine——清除函数,arg——传给清除函数的参数。
#include <pthread.h> void pthread_cleanup_pop(int execute);
参数:execute——0:不执行push设置的清除函数;其他值执行。
问:这两个函数如何使用?有什么作用?
答:从调用pthread_cleanup_push()开始,到调用pthread_cleanup_pop()之前,期间如果线程中有调用pthread_exit()函数、或有异常退出,都将调用清除函数。特别注意:期间如果线程是执行return语句退出的绝不执行清除函数。pthread_cleanup_pop()函数参数只要非0就可“强行”执行清除函数。在设计程序时一个push对应一个pop,遵循先进后出原则。此外,在编写程序的时候如果push和pop不成对存在,编译怎么都是通不过的。作用:防止线程异常退出造成内存泄漏。
三、例程编写测试
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <unistd.h> 4 5 /*线程清理函数*/ 6 void *mythread_clean(void *arg) 7 { 8 printf("%s\n",(char *)arg); 9 return (void *)0; 10 } 11 12 /*线程执行函数*/ 13 void *mythread(void *arg) 14 { 15 printf("running in mythread.\n"); 16 17 //将线程清理函数压入清除栈两次 18 pthread_cleanup_push( (void*)mythread_clean,"running in mythread clean handler." ); 19 printf("mythread push complete.\n"); 20 //如果参数为空--主动退出线程 21 if(!arg) 22 pthread_exit((void *)1); 23 pthread_cleanup_pop(0); 24 25 // 打印线程接收到的字符串参数 26 printf("*arg:%s\n",(char *)arg); 27 printf("sleep......\n"); 28 sleep(2); 29 printf("exit......\n"); 30 pthread_exit((void *)1); 31 } 32 33 int main(int argc,char *argv[]) 34 { 35 int err; 36 pthread_t tid; 37 void *tret; 38 39 if(argc != 2) 40 { 41 printf("arg error.\n"); 42 printf("ture usage:./app xxx\n"); 43 } 44 //创建线程并执行线程执行函数 45 err=pthread_create(&tid,NULL,mythread,argv[1]); 46 if(err!=0) 47 { 48 printf("thread create error.\n"); 49 return -1; 50 } 51 52 //阻塞等待线程退出,并获取线程的返回值 53 err=pthread_join(tid,&tret); 54 if(err!=0) 55 { 56 printf("error .... \n"); 57 return -1; 58 } 59 printf("mythread return value %d.\n",(int)tret); 60 61 return 0; 62 }
编译要点:pthread并非Linux系统的默认库,而是POSIX线程库。在编译的时候加上“-lpthread”选项。
测试结果:
编译时的那个警告是因为程序中把一个char型指针强制转换成int型变量值。