Chinaunix首页 | 论坛 | 博客
  • 博客访问: 805454
  • 博文数量: 106
  • 博客积分: 1250
  • 博客等级: 少尉
  • 技术积分: 1349
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-09 09:38
文章分类

全部博文(106)

文章存档

2014年(1)

2013年(13)

2012年(92)

分类:

2012-05-02 18:26:11

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

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 );

阅读(1067) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~