Linux---浅析Linux下的线程

一、线程

线程的概念:

  • 在一个程序里的执行路线就叫做线程,或者说线程是一个进程内部的执行流
  • 一个进程至少有一个执行流,也就是主线程
  • 线程和线程之间大部分资源都是共享的
Linux下的进程与线程
  • 图解:

这里写图片描述

线程和进程对比来看:

  • 线程在进程内部执行
  • 线程比进程执行流更小,执行粒度更小
  • 线程的切换比进程切换成本小
  • 线程的创建比进程轻量化很多,所以创建和释放成本都非常低,而且Linux下是没有具体的线程的,Linux内核同样用PCB来描述线程,只不过同组线程具有相同的pid(进程ID),所以Linux下的线程被看做是轻量化进程
  • 线程出错也相当于进程出错,也就是某个线程因为中断,异常等挂掉,该进程也会挂掉

进程内线程共享的资源:

  • 同一地址空间,代码段和数据段都是共享的,在一个程序内定义一个函数或者全局变量,该进程内的所有线程都可以调用该函数,访问该变量
  • 文件描述符表
  • 每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)
  • 当前工作目录
  • 用户ID和组ID

各线程独享的资源

  • 私有栈:一个线程调用一个函数时,该函数内部的局部变量等都在该进程私有的栈空间内,其他线程无权访问
  • 独立上下文:线程被切换时,它的上下文数据都是自己独有的,等到被切换回来时上下文数据也会恢复,继续执行之前的程序
  • 信号屏蔽字
  • 线程ID(不同于进程ID,每个线程都有自己独有的标识符)等

线程控制

POSIX线程库
  • 绝大多数与线程相关的函数都是以”pthread_”开头的
  • 使用这些函数时,需要引入头文件”pthread.h”
  • 链接这些线程函数库时要使用编译器命令的”-lpthread”选项
创建线程

pthread_create函数

函数体:

        int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);  
thread:返回线程ID
attr:设置线程的属性,attr为NULL时表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码

错误检查:

  • 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误
  • pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回
  • pthreads同样也提供了线程内的errno变量,因为读取返回值要比读取线程内的errno变量的开销更小
线程终止

如果是只终止某个线程,而不终止整个进程,则有下面的三种方法

  • 线程调用的函数通过return返回,但对主线程不适用,从main函数调用return则相当于整个进程终止
  • 线程调用pthread_exit函数终止自己,注意线程调用的函数调用exit时,会使整个进程终止
  • 一个线程通过调用pthread_cancelt函数终结别的进程

pthread_exit函数

函数体:

      void pthread_exit(void *value_ptr);  
value_ptr:value_ptr不要指向一个局部变量,因为当其他线程得到这个返回指针时线程函数已经退出了
返回值:无返回值,和进程一样,线程结束的时候无法返回到它的调用者(自身)

pthread_cancel函数

函数体:

       int pthread_cancel(pthread_t thread);  
thread:线程ID
返回值:成功返回0,失败返回错误码
线程等待

一个线程退出后,其空间并不会自动释放,必须有另外一个线程来回收其资源,并且创建新的线程不会占用刚退出的线程的地址空间,

pthread_join函数

函数体:

     int pthread_join(pthread_t thread, void **value_ptr);  
thread:线程ID
value_ptr:它指向一个指针,后者返回线程的返回值
返回值:成功返回0,失败返回错误码
线程分离

线程在调用join方法时,进行的是阻塞式的等待,所以当我们不关心线程的返回值时,join方法是一种负担,我们就可以通过调用线程分离函数来使线程退出时,自动释放线程资源

需要额外注意的是,即使线程被分离,可线程出现异常时,仍然会影响进程,因为线程始终是在进程内执行的

在线程已经分离后,再有别的进程去调用join方法去等待其退出时,join方法会返回错误码,因为一个分离的线程是不能被其他线程回收或杀死的

pthread_detach函数

函数体:

      int pthread_detach(pthread_t thread);  
线程组内其他线程对目标线程进行分离
       pthread_detach(pthread_self());
线程自己分离

线程ID与进程ID

  • 一个程序下的所有线程同属于一个组,简称线程组,即多线程的进程,这些线程因为同属于一个进程,所以拥有相同的进程ID(PID),而每个线程又拥有各自的进程描述符(因为Linux下,线程被看做是轻量级进程),即线程ID
    void *thread_run(void *arg)
    {
        const char* msg = (const char*)arg;
        while(1){//通过死循环控制不让线程终止
        printf("I am %s ,my id is %d\n",msg,syscall(SYS_gettid));
        }
    }

    int main()
    {
        pthread_t tid1,tid2,tid3,tid4;
        //创建四个线程,该进程下就会拥有五个线程
        pthread_create(&tid1,NULL,thread_run,"thread1");
        pthread_create(&tid2,NULL,thread_run,"thread2");
        pthread_create(&tid3,NULL,thread_run,"thread3");
        pthread_create(&tid4,NULL,thread_run,"thread4");



        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        pthread_join(tid3,NULL);
        pthread_join(tid4,NULL);


    }  

查看当前的线程状况
这里写图片描述

  • LWP(Light Weighted Process)线程ID,同组的线程的进程ID是相同的,这两个概念不要搞混了
  • NLWP:线程组内线程的个数

线程ID不同于pthread_t类型的ID,线程ID是pid_t类型的变量,而且是用来唯一标识线程的一个整形变量,pthread_t类型的ID只是一个地址

为什么有两个thread ID

线程库实际上由两部分组成:内核的线程支持+用户态的库支持(glibc)。Linux在早期内核不支持线程的时候,glibc就在库中(用户态)以线程(就是用户态线程)的方式支持多线程了。POSIX thread只对用户编程的调用接口作了要求,而对内核接口没有要求。linux上的线程实现就是在内核支持的基础上,以POSIX thread的方式对外封装了接口,所以才会有两个ID的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值