线程——线程控制

库是基于系统调用接口

POSIX线程库

·与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_“开头

·要使用这些函数库,必须要引入头文件

·链接这些线程函数是要使用编译器命令“-lpthread”选项

一、创建线程

功能:创建一个新的线程

原型:


参数:

        thread—返回线程ID

        attr—设置线程的属性,attr为NULL表示使用默认属性

        start_routine—是个函数地址,线程启动后要执行的函数

        arg—传给线程启动函数的参数    

返回值:成功返回0,失败返回错误码

错误检查:

·传统的一些函数是成功返回0,失败返回-1,并且对全局变量error赋值以指示错误

·pthread函数出错时不会设置全局变量error,而是将错误代码通过返回值返回

·pthreads同样也提供了线程内的error变量,以支持其他使用error代码。对于pthreads函数的错误,建议通过返回值要比读取线程内的error变量的开销更小

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<pthread.h>
  6 
  7 void* root(void* arg)
  8 {
  9     while(1){
 10         printf("I am pthread 1\n");
 11         sleep(1);
 12     }
 13 }
 14 int main()
 15 {
 16     pthread_t tid;
 17     int ret = pthread_create(&tid,NULL,root,NULL);
 18     if(ret != 0){
 19         perror("pthread_create");
 20         return -1;
 21     }
 22     while(1){
 23         printf("I am main pthread!\n");
 24         sleep(1);
 25     }
 26     return 0;
 27 }

进程ID和线程ID

·在Linux中,目前的线程实现是Native POSIX Thread Libaray,简称NPTL。在这种实现下,线程又被称为轻量级进程(Light Weighted Process),每一个用户态的线程,在内核中都对应一个调度实体,也拥有自己的进程描述符(task_struct)

·没有线程之前,一个进程对应内核里的一个进程描述符,对应一个进程ID。但是引入线程的概念后,情况发生了变化,一个用户进程下管理N个用户态线程,每个线程作为一个独立的调度实体在内核态里都有自己的进程描述符,进程和内核变成了1:N关系

struct task_struct{
  ...
  pid_t pid;
  pid_t tgid;
  ...
  struct task_struct *group_leader;
  ...
  struct list_head thread_group;
  ...
};

·多线程的进程又被称为线程组,线程组内的每个线程在内核之中都存在一个进程描述符(task_struct)与之对应。进程描述符结构体中的pid,表面上看对应的是进程ID,其实不然,它对应的是线程ID,进程描述符中的tgid,含义是Thread Group ID ,该值对应的是用户层面的进程ID


查看线程ID:

方法一:


方法二:



方法三:调用函数


 14 void thread_run(void* arg)
 15 {
 16     while(1){
 17         printf("this is run ...,tid=%d\n",pthread_self());
 18         sleep(1);
 19     }
 20 }

我们发现这个线程ID特别大,为什么与之前的不一样?

pthread_create函数产生的线程ID,存放在第一个参数指向的地址中。该线程ID与前面的线程ID不是一回事。

前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度的最小单位,所以需要一个数值来唯一表示该线程。

pthread_self()函数的返回值的类型是pthread_t,对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。

二、线程终止

如果只需要终止某个线程而不终止整个进程,有三种方法:

(1)从线程函数return

(2)线程可以调用pthread_exit终止自己

(3)一个线程可以调用pthread_cancel终止同一个进程中的另一个线程

pthread_exit函数

功能:线程终止

原型:


参数:*retval—retval不要只想一个局部变量

返回值:无返回值,跟进程一样,线程结束时无法返回到它的调用者

注意:pthread_exit或者return返回的指针所指向的内存单元必须是用malloc分配的,不能在线程函数的栈上分配,因为当其他线程得到这个返回指针时线程函数已经退出了

pthread_cancel函数

功能:取消执行中的线程

原型:


参数:thread——线程ID

返回值:成功返回0,失败返回错误码

三、线程等待

原因:a、已经退出的线程,其空间没有被释放,仍然在进程的地址空间内

         b、创建新的进程不会复用刚才退出线程的地址空间

功能:等待线程结束

原型:


参数:thread—线程ID

         retval—它指向一个指针,后者指向线程的返回值

返回值:成功返回0,失败返回错误码

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<unistd.h>
  5 #include<pthread.h>
  6 
  7 void* thread1(void* arg)
  8 {
  9     printf("thread1 returning ...\n");
 10     int *p = (int*)malloc(sizeof(int));
 11     *p = 1;
 12     return (void*)p;
 13 }
 14 void* thread2(void* arg)
 15 {
 16     printf("thread2 exitint ...\n");
 17     int *p = (int*)malloc(sizeof(int));
 18     *p = 2;
 19     pthread_exit((void*)p);
 20 }
 21 void* thread3(void* arg)
 22 {
 23     while(1){
 24         printf("thread3 runing ...\n");
 25         sleep(2);
 26     }
 27     return NULL;
 28 }
 29 int main()
 30 {
 31     pthread_t tid;
 32     void* ret;
 33 
 34     //thread1 return
 35     pthread_create(&tid,NULL,thread1,NULL);
 36     pthread_join(tid,&ret);
 37     printf("thread1 return,thread1 id %X,return code:%d\n",tid,*(int*)ret);
 38     free(ret);
 39 
 40     //thread2 exit
 41     pthread_create(&tid,NULL,thread2,NULL);
 42     pthread_join(tid,&ret);
 43     printf("thread2 return,thread2 id %X,return code:%d\n",tid,*(int*)ret);
 44     free(ret);
 45 
 46     //thread3 cancel by other
 47     pthread_create(&tid,NULL,thread3,NULL);
 48     sleep(3);
 49     pthread_cancel(tid);
 50     pthread_join(tid,&ret);
 51     if(ret == PTHREAD_CANCELED){
 52         printf("thread3 return,thread3 id %X,return code:PTHREAD_CANCELED\n",tid);
 53     }
 54     else{
 55         printf("thread3 return,thread3 id %X,return code:NULL\n",tid);
 56     }
 57     return 0;
 58 }

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

1、如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。

2、如果thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的常数PTHREAD_CANCELED

3、如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数

4、如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数

四、线程分离

·默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏

·如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源


可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离

pthread_detach(pthread_self());
joinable和分离是冲突的,一个线程不能即是joinable又是分离的
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<string.h>
  4 #include<unistd.h>
  5 #include<pthread.h>
  6 
  7 void* pthread_run(void* arg)
  8 {
  9     pthread_detach(pthread_self());
 10     printf("%s\n",(char*)arg);
 11     return NULL;
 12 }
 13 int main()
 14 {
 15     pthread_t tid;
 16     if(pthread_create(&tid,NULL,pthread_run,"thread1 run ...") != 0){
 17         printf("create thread error\n");
 18         return 1;
 19     }
 20     int ret = 0;
 21     sleep(1);
 22     if(pthread_join(tid,NULL) == 0){
 23         printf("pthread wait success\n");
 24         ret = 0;
 25     }
 26     else{
 27         printf("pthread wait failed\n");
 28         ret = 1;
 29     }
 30     return ret;
 31 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值