Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3514846
  • 博文数量: 1805
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:01
文章分类

全部博文(1805)

文章存档

2017年(19)

2016年(80)

2015年(341)

2014年(438)

2013年(349)

2012年(332)

2011年(248)

分类: LINUX

2013-03-10 21:13:51

原文地址:线程基本操作 作者:yulianliu1218




1.创建线程
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrit attr,
                   void *(*start_rtn)(void),void *restrict arg);
 
当函数返回时,tidp指向内核分配给线程的ID,attr表示线程的属性,一般设为NULL,
start_rtn是个函数指针,start_rtn指向的函数是线程要执行的函数,一旦pthread_create成功返回,线程创建,就从start_rtn指向的函数开始执行代码。当从这个函数返回时,线程就停止运行了。
arg是start_rtn所指向的函数的参数,在线程开始执行时,该参数由内核负责传递给线程。
 
pthread_create函数成功调用返回0.
如果创建线程失败,返回的是错误编号。
 
2.向线程体函数传递参数
pthread_creat函数在创建线程时由内核向新线程传递参数,如果需要传递多个参数则需要将所有参数组织在一个结构体内,再将结构体的地址作为参数传给新线程。

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <pthread.h>

  5. #define BUFS PIPE_BUF //PIPE_BUF管道默认一次性读写的数据长度


  6. struct arg_struct {
  7.     char arg1[10];
  8.     int arg2;
  9.     float arg3;
  10. };

  11. typedef struct arg_struct ARG;

  12. void *thread_func(void *arg)
  13. {
  14.     ARG *p=(ARG *)arg;
  15.     printf("arg1 is : %s,arg2 is:%d, arg3 is :%f\n",p->arg1,p->arg2,p->arg3);
  16.     
  17.     return NULL;
  18. }

  19. int main(int argc,char *argv[])
  20. {
  21.     pthread_t tid;
  22.     ARG arg;
  23.     int err;
  24.     
  25.     strcpy(arg.arg1,argv[1]);
  26.     arg.arg2 = atoi(argv[2]);
  27.     arg.arg3 = atof(argv[3]);

  28.     
  29.     err = pthread_create(&tid,NULL,thread_func,(void *)&arg);
  30.     
  31.     if(err !=0)
  32.     {
  33.         printf("create thread thread_func failed:%s\n",strerror(err));
  34.         exit(1);
  35.     }
  36.     
  37.     return 0;
  38. }
编译程序:#gcc -lpthread struct_arg.c -o struct_arg
要加上pthread库 -lpthread
 
3.线程访问资源的限制
在线程执行的过程中可以访问进程的资源,下面的例子在main函数创建了一个结构体,并初始化之,该结构体既有栈数据成员指针,又有堆数据成员 指针。main线程睡眠10s,让子线程先执行,在线程thread_func()执行后,结构体数据增加。main线程使用的是已经发生改变的数据。

 

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <pthread.h>


  4. typedef struct arg_struct{
  5.     int *heap;
  6.     int *stack;
  7. }ARG;

  8. FILE *fp=NULL;

  9. void *thread_func(void *arg)
  10. {
  11.     ARG *p;
  12.     p=(ARG *)arg;
  13.     
  14.     (*p->heap)++; //堆数据递增
  15.     (*p->stack)++; //栈数据递增
  16.     
  17.     fprintf(fp,"new thread heap:%d , stack:%d\n",*(p->heap),*(p->stack));
  18.     printf("the new thread done\n");
  19.     
  20.     return NULL;
  21. }

  22. int main(void)
  23. {
  24.     pthread_t tid;
  25.     ARG arg;
  26.     int *heap;
  27.     int stack;
  28.     int err;
  29.     
  30.     heap=(int *)malloc(1*sizeof(int));
  31.     
  32.     if(heap==NULL)
  33.     {
  34.         perror("failed to malloc!\n");
  35.         exit(1);
  36.     }
  37.     
  38.     *heap = 2;
  39.     stack = 3;
  40.     
  41.     arg.heap=heap;
  42.     arg.stack=&stack;
  43.     
  44.     if((fp=fopen("test.txt","wb"))==NULL){
  45.         perror("fail to open");
  46.         exit(1);
  47.     }
  48.     
  49.     err = pthread_create(&tid, NULL,thread_func,(void *)&arg);
  50.     if(err!=0)
  51.     {
  52.         printf("failed to create thread %s\n",strerror(err));
  53.         exit(1);
  54.     }
  55.     
  56.     sleep(10);
  57.     
  58.     (*heap)++;
  59.     stack++;
  60.     
  61.     fprintf(fp,"main thread: heap:%d,stack:%d\n",*(arg.heap),*(arg.stack));
  62.     printf("the main thread done!\n");
  63.     
  64.     fclose(fp);
  65.     
  66.     free(heap);
  67.     
  68.     exit(0);
  69. }

  70. test.txt文件的内容:
  71. new thread heap:3 , stack:4
  72. main thread: heap:4,stack:5

5.终止线程
线程的退出方式有三种:
(1)线程体函数从启动线程中返回,return
(2)线程被另一个线程终止。类似于一个进程被另一个进程调用kill函数杀死
(3)线程自行调用pthread_exit()退出。
pthread_exit()用于线程退出,可以指定返回值,以便其他线程通过pthread_join()函数获取该线程的返回值
return,是函数返回,不一定是线程函数。 只有线程函数return,线程才会退出
exit()是进程退出,如果在线程函数中调用exit,那该线程的进程也就会退出。。。会导致该线程所在进程的其他线程退出。慎重使用exit()。

linux环境下使用pthread_exit来终止进程,
#include
void pthread_exit(void *rval_ptr);
参数是一个指向任意类型的指针,该指针指向的区域存储退出信息,该信息类似于传递给新线程的参数,可以将多个信息组织成一个结构体。
 
当一个线程结束运行时,其结束信息的地址保存在内核中,其他的线程可以引用此线程的结束信息。
linux环境下使用pthread_join函数来访问指定线程的结束信息。
int pthread_join(pthread_t tid,void **rval_ptr);
 
第一个参数表示需要取得结束信息的线程,如果该线程还在运行中,那么pthread_join函数会导致调用线程的阻塞,直到指定的线程结束执 行为止。如果指定线程的线程ID和调用线程不属于同一个进程,则pthread_join函数出错返回。这一点说明不同进程的线程之间的通信远不像属于同 一进程的线程那样简单,事实上其通信方式类似于进程之间的通信方式。
 
第二个参数是一个指向任意类型指针的指针。由于pthread_join函数负责从内核中得到指定线程结束信息的地址,这是一个指针,因为要在内核中改变它的值,所以该参数的类型为指针的指针。如果线程由于线程体函数返回或者调用pthread_exit函数退出,则rval_ptr指向的是退出信息的首地址,如果线程由于被其他线程取消而退出,则rval_ptr被设置为PTHREAD_CANCELED.
 
如果调用线程对指定线程的退出信息并不关心,可以讲rval_ptr参数设置为NULL,这时调用线程仅仅等待指定线程结束执行,而不获得线程退出信息。如果成功得到指定线程的退出信息,pthread_join函数返回0,失败则返回错误号。
 

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <pthread.h>
  4. #include <unistd.h>

  5. void *func1(void *arg)
  6. {
  7.     printf("the first\n");
  8.     return (void *)1;
  9. }

  10. void *func2(void *arg)
  11. {
  12.     printf("the second\n");
  13.     pthread_exit((void*)3);
  14.     printf("should not be here!\n");
  15. }

  16. void *func3(void *arg)
  17. {
  18.     sleep(10);
  19.     printf("the third,sleep 10 seconds\n");
  20.     
  21.     return NULL;
  22. }

  23. int main(void)
  24. {
  25.    pthread_t tid1,tid2,tid3;
  26.    void *res;
  27.    int err;
  28.     
  29.     //thread1
  30.     err = pthread_create(&tid1, NULL,func1,NULL); //创建第一个线程
  31.     if(err!=0)
  32.     {
  33.         printf("failed to create thread %s\n",strerror(err));
  34.         exit(1);
  35.     }
  36.     
  37.     err = pthread_join(tid1,&res); //等待线程退出,并获得退出消息
  38.     if(err!=0)
  39.     {
  40.         printf("can't join thread1 %s\n",strerror(err));
  41.         exit(1);
  42.     }
  43.     printf("result from thread1:%d\n",(unsigned int )(res));//输出提示信息
  44.     
  45.     //thread2
  46.     err = pthread_create(&tid2, NULL,func2,NULL);
  47.     if(err!=0)
  48.     {
  49.         printf("failed to create thread %s\n",strerror(err));
  50.         exit(1);
  51.     }
  52.     
  53.     err = pthread_join(tid2,&res); //等待线程退出,并获得退出消息
  54.     if(err!=0)
  55.     {
  56.         printf("can't join thread2 %s\n",strerror(err));
  57.         exit(1);
  58.     }
  59.     printf("result from thread2:%d\n",(unsigned int )(res));//输出提示信息
  60.     
  61.     
  62.     //thread3
  63.     err = pthread_create(&tid3, NULL,func3,NULL);
  64.     if(err!=0)
  65.     {
  66.         printf("failed to create thread %s\n",strerror(err));
  67.         exit(1);
  68.     }
  69.     
  70.     err = pthread_join(tid3,&res); //等待线程退出,并获得退出消息
  71.     if(err!=0)
  72.     {
  73.         printf("can't join thread2 %s\n",strerror(err));
  74.         exit(1);
  75.     }
  76.     printf("result from thread3:%d\n",(unsigned int )(res));//输出提示信息
  77.     
  78.     printf("the third thread is done\n");
  79.     return 0;
  80.     
  81. }

  82. the first
  83. result from thread1:1
  84. the second
  85. result from thread2:3
  86. (10s钟之后)
  87. the third,sleep 10 seconds
  88. result from thread3:0
  89. the third thread is done

6.正确得到线程退出信息的方法
在线程结束运行后,linux内核中保存的只是存储退出信息内存区域的首地址,而并未将退出信息实际保存在内核中,因此,在线程结束运行后,其保存退出信息的内存区域仍然是有效的。所以不能将退出信息存储在局部变量中,而应该使用动态分配的内存或者全局变量。
 
就是说数据不要保存在函数的栈帧中,
 
7.取消一个线程的运行
一个进程可以通过发送信号的方式使另一个进程结束运行,如调用kill函数发送SIGKILL信号。同进程一样,一个线程也可以被另一个线程取消掉。
linux环境下使用pthread_cancel函数取消另一个线程
int pthread_cancel(pthread_t tid);
 
参数是实际要取消线程的ID,取消成功,返回0,失败返回错误编号。
调用该函数等效于线程自己调用pthread_exit(PTHREAD_CANCELED);
 
8.线程退出函数
同进程一样,一个线程在退出时可以调用用户设置好的函数,这些函数称为线程清理程序,记录在栈中,linux环境下使用pthread_cleanup_push函数添加一个清理程序记录,使用pthread_cleanup_pop函数调用清理程序。
void pthread_cleanup_push(void (*rtn)(void *),void *arg);
void pthread_cleanup_pop(int execute);
 
push函数的第一个参数是一个函数指针,指向清理程序,清理程序是一个没有返回值的函数,其参数是一个任意类型的指针。
pop函数的参数表示是否执行栈顶的清理程序,参数execute为0,表示不执行,但是将栈顶的清理程序记录出栈;参数非零,表示执行栈顶清理程序,执行之后该记录也会出栈。
 
从pthread_cleanup_push的调用点到pthread_cleanup_pop 之间的程序段中的终止动作(包括pthread_exit和异常终止,不包括return)都将执行pthread_cleanup_push所指定的清理函数。
 
PS:
linux环境,sleep可以用于线程或进程的睡眠。
 sleep的安全性问题:(从网上搜得)
unix 环境高级编程有对sleep详细说明

在线程中使用sleep 不安全的原因是在有些平台下sleep是用alarm函数实现的,与信号相关,而信号是针对于进程的, 在线程下使用不安全。

FreeBSD5.2.1、linux 2.4.22和Mac 0SX 10.3 用nanosleep提供延迟。该函数可以使sleep的实现与信号无关.

还有一法,select:
select( 1, NULL, NULL, NULL, & timeout );
阅读(456) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~