【Linux】线程(概念、特点、线程控制代码--创建/等待/终止、分离与结合属性)

来源:互联网 发布:浙江大学王灿数据挖掘 编辑:程序博客网 时间:2024/05/21 11:05

一、进程和线程

进程和线程的概念:

进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。

线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程由几个线程组成(拥有很多相对独立的执行流的用户程序共享应用程序的大部分数据结构),线程与同属一个进程的其他的线程共享进程所拥有的全部资源。

进程与线程的区别:

“进程——资源分配的最小单位,线程——程序执行的最小单位”

  1. 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。

  2. 线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

  3. 对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

总的来说就是:进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。

二、linux下线程的特点:

  1. Linux的线程是通过进程来模拟的,也就是说Linux里的线程本质上就是进程。

  2. Linux的线程机制是通过内核和库混合实现的,所以线程的实现在 Linux的核心态和用户态都有执行,内核实现线程/进程的调度 libpthread库实现线程之间的同步。这也就是为什么多线程程序需要连接一个libpthread库的原因。

  3. Linux这种用进程模拟线程的方式,和signal机制不一致,signal是发
    给进程的,但是在linux里,往一个进车发送signal,实际上只有一个线程处理这个signal。

  4. Linux程序如果用pthread_create启动一个新的线程,实际上启动了两个轻量进程,第一个是管理线程,第二个才是真正做事情的线程。但是后续新创建的线程就不需要再创建管理线程了。

  5. 在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种”昂贵”的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。

三、关于线程的代码

创建线程

这里写图片描述

头文件 #include

```#include<stdio.h>#include<stdlib.h>#include<pthread.h>pthread_t tid;void* thread_run(void *_val){       printf("%s :pid is:%d,tid is :%d\n",(char *)_val,(int)getpid(),(unsigned long long)pthread_self());       return NULL;}int main(){      int err = pthread_create(&tid,NULL,thread_run,NULL);       if(err!=0)     {         printf("create thread error!info is :%s\n",strerror(err));         exit(err);              }       printf("main thread run:pid is :%d,tid is:%d\n",(int)getpid(),(unsigned long long)pthread_self());       sleep(1);       return 0;}

运行结果

这里写图片描述

可知在linux下,pthread_t类型是一个地址值,属于同一进程的多个线程调用getpid(2)可以得到相同的进程号,而调用pthread _self得到的线程号各不相同。

线程终止

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

  1. 从线程函数return,main()函数return相当于exit;
  2. 一个线程可以调用pthread_cancel终止同一进程中的某个线程
  3. 线程可以调用pthresd_exit来终止自己
    这里只介绍pthread_exit的用法

这里写图片描述

这里retval是void*类型,用法和上面void *相同,其它线程可以调用pthread_join获得这个指针
注意
pthread_exit和return返回的指针所指向的内存单元只能是全局的或者是malloc分配的,不能在线程函数的栈上分配,因为当其他线程得到这个返回指针时,线程函数已经退出了。

线程等待

这里写图片描述

返回值,成功返回1,失败返回错误码
调用该函数的线程将挂起等待,直到id为thread的线程终止。
参数
thread,被创建线程的标识符,线程等待中pthread_join使用这个标识符来等待该线程的结束。
第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。

代码

#include<stdio.h>#include<stdlib.h>#include<pthread.h>void* thread1(void *_val){  printf("thread 1 returning...\n");return(void*)1;}void* thread2(void *_val){  printf("thread 2 returning...\n");  pthread_exit((void*)2);}void* thread3(void *_val){while(1){   printf("thread 3 returning.wait be cancal...\n");  sleep(1);}   return NULL;}int main(){     pthread_t tid;    void *tret;//thread1 return     pthread_create(&tid,NULL,thread1,NULL);     pthread_join(tid,&tret);         printf("thread return,thread id is:%u,return code is:%d\n",(unsigned long)tid,(int)tret);//thread2 exit     pthread_create(&tid,NULL,thread2,NULL);     pthread_join(tid,&tret);     printf("thread return,thread id is:%u,return code is:%d\n",(unsigned long)tid,(int)tret);//thread3 cancel by other     pthread_create(&tid,NULL,thread3,NULL);     sleep(3);     pthread_cancel(tid);     pthread_join(tid,&tret);     printf("thread return,thread id is:%u,return code is:%d\n",(unsigned long)tid,(int)tret);     return 0;}

运行结果
这里写图片描述

可见,在linux库中,常数PTHREAD_CANCELED的取值是-1,一般情况下,其终止状态一般保留到其他线程调用pthread _join获取它的状态为止。

四、线程的分离与结合属性

在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。

     默认创建线程是可结合的,一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的,所以主线程必须等待子线程回收其状态。     相反,若一个线程变为可分离的,这个线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。所以它此时与主线程关系不大,但当当前进程退出时,该分离线程也会退出,因为虽然其已分离,但其资源依然为当前进程的,进程退出资源回收,其必定也随之退出。
原创粉丝点击