zoukankan      html  css  js  c++  java
  • 【linux】系统编程-5-线程


    前言

    7. 线程

    7.1 概念

    • 进程:进程是资源管理的最小单位

    • 线程:线程是程序执行的最小单位

    • 因为进程开销大,才衍生出线程

      • 进程切换上下文时, 需要重新映射虚拟地址空间、进出OS内核、寄存器切换,还会干扰处理器的缓存机制
    • 一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等), 而将线程分配到某个cpu上执行

    • 新的执行线程将拥有自己的栈,但与它的创建者共享全局变量、文件描述符、信号处理函数和当前目录状态

    • 特点:

      • 一个程序至少有一个进程,一个进程至少有一个线程
      • 线程使用进程的资源,进程崩溃,线程也随之崩溃
      • 线程切换上下文快,进程切换上下文慢
    • 使用 pthread_create 创建线程

    • 使用 int pthread_attr_destroy(pthread_attr_t *attr); 函数来销毁一个线程属性对象

    • 使用 pthread_attr_init() 函数可以初始化线程对象的属性

    • 使用 pthread_join() 等待线程结束

    • 使用 pthread_detach() 来设置线程为分离状态

    7.2 创建线程

    7.2.1 pthread_create()

    • 使用 pthread_create 创建线程
    • 通过命令 man 了解更多
    • 所需要的头文件:
      #include <pthread.h>
      
    • 函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
      • thread:指向线程标识符的指针
      • attr:设置线程属性
      • start_routine:函数指针,线程入口
      • arg:传给线程入口函数的参数

    7.3 设置线程属性

    • 线程属性结构体 pthread_attr_t
      typedef struct{
          int                   etachstate;      //线程的分离状态
          int                   schedpolicy;     //线程调度策略
          structsched_param     schedparam;      //线程的调度参数
          int                   inheritsched;    //线程的继承性
          int                   scope;           //线程的作用域
          size_t                guardsize;       //线程栈末尾的警戒缓冲区大小
          int                   stackaddr_set;   //线程的栈设置
          void*                 stackaddr;       //线程栈的位置
          size_t                stacksize;       //线程栈的大小
      }pthread_attr_t;
      
    • 注意:
      • 因为 pthread 并非 Linux 系统的默认库,而是 POSIX线程库。在Linux中将其作为一个库来使用, 因此编译时需要加上-lpthread(或-pthread)以显式指定链接该库
      • 函数在执行错误时,并不把错误信息写到变量 error 中,而是作为返回值返回
    • 线程属性不能直接设置,只能通过函数操作
    • 线程属性包括:作用域(scope)、栈大小(stacksize)、栈地址(stackaddress)、优先级(priority)、 分离的状态(detachedstate)、调度策略和参数(scheduling policy and parameters)
    • 线程的默认属性:非绑定、非分离、1M的堆栈大小、与父进程同样级别的优先级
    • API:
    API 说明
    pthread_attr_init() 初始化一个线程对象的属性
    pthread_attr_destroy() 销毁一个线程属性对象
    pthread_attr_getaffinity_np() 获取线程间的CPU亲缘性
    pthread_attr_setaffinity_np() 设置线程的CPU亲缘性
    pthread_attr_getdetachstate() 获取线程分离状态属性
    pthread_attr_setdetachstate() 修改线程分离状态属性
    pthread_attr_getguardsize() 获取线程的栈保护区大小
    pthread_attr_setguardsize() 设置线程的栈保护区大小
    pthread_attr_getscope() 获取线程的作用域
    pthread_attr_setscope() 设置线程的作用域
    pthread_attr_getstack() 获取线程的堆栈信息(栈地址和栈大小)
    pthread_attr_setstack() 设置线程堆栈区
    pthread_attr_getstacksize() 获取线程堆栈大小
    pthread_attr_setstacksize() 设置线程堆栈大小
    pthread_attr_getschedpolicy() 获取线程的调度策略
    pthread_attr_setschedpolicy() 设置线程的调度策略
    pthread_attr_setschedparam() 获取线程的调度优先级
    pthread_attr_getschedparam() 设置线程的调度优先级
    pthread_attr_getinheritsched() 获取线程是否继承调度属性
    pthread_attr_getinheritsched() 设置线程是否继承调度属性

    7.3.1 pthread_attr_init()

    • 使用 pthread_attr_init 初始化线程对象属性
    • 通过命令 man 了解更多
    • 所需要的头文件:
      #include <pthread.h>
      
    • 函数原型:int pthread_attr_init(pthread_attr_t *attr);
      • attr:指向一个线程属性的指针
      • 返回:
        • 成功:返回 0
        • 失败:返回 非 0

    7.3.2 销毁一个线程属性对象

    • 使用 int pthread_attr_destroy(pthread_attr_t *attr); 函数来销毁一个线程属性对象
      • attr:指向一个线程属性的指针
      • 返回:
        • 成功:返回 0
        • 失败:返回 错误码

    7.3.3 线程的分离状态

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

      • 可结合的线程:能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的
      • 分离的线程:不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放
    • 线程的分离状态决定一个线程以什么样的方式来终止自己

    • 默认状态下是 非分离状态

    • 函数:

      • int pthread_join(pthread_t tid, void **rval_ptr);
        • 等待某个线程的终止,获得该线程的终止状态,并收回所占的资源
          • tid:线程标识符
        • rval_ptr设置为NULL,则忽略返回状态
      • int pthread_detach(pthread_t tid);
        • 将线程设置为分离状态
        • 头文件:#include <pthread.h>
        • tid:线程标识符
        • 返回:
          • 成功:返回 0
          • 失败:返回一个错误值:
            • EINVAL:tid 对应的线程不是一个 非分离的线程
            • ESRCH:找不到对应的线程
    • 非分离状态下:

      • 原有线程等待创建的线程结束,只有当 pthread_join() 函数放回时,创建的线程才算真正终止
    • 分离状态下:

      • 该线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源
    • 如果在创建线程时就知道不需要了解线程的终止状态,则可以 pthread_attr_t 结构中的 detachstate 线程属性,让线程以分离状态启动

      • 使用 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) 函数来设置线程分离状态
        • attr:指向一个线程属性的指针
        • detachstate
          • PTHREAD_CREATE_DETACHED:分离线程
          • PTHREAD _CREATE_JOINABLE:非分离线程
    • 获取某个线程的分离状态

      • 使用 int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); 函数获取线程的分离状态
      • attr:指向一个线程属性的指针
      • detachstate:保存状态的值
      • 返回:
        • 成功:返回 0
        • 失败:返回 错误码
    • 注意:

      • 如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create 函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用 pthread_create 的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait 函数,让这个线程等待一会儿,留出足够的时间让函数 pthread_create 返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如 wait() 之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

    7.3.4 线程的调度策略

    • POSIX 标准指定了三种调度策略:

      1. 普通线程(默认)SCHED_OTHER。采用时分调度策略,不需要实时机制。另外两种用于超级用户权限时运行。
      2. 实时线程SCHDE_FIFO。采用实时调度-先进先出方式策略。一旦占用cpu则一直运行,直到有更高优先级任务到达或自己放弃。
      3. 轮询调度SCHED_RR。采用实时调度-时间片轮转方式调度策略。当任务的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。所以说SCHED_RR=SCHED_OTHER+SCHDE_FIFO。
    • 与调度相关的API

      • 参数说明:
        • attr:指向一个线程属性的指针。
        • inheritsched:线程是否继承调度属性,可选值分别为
          • PTHREAD_INHERIT_SCHED:调度属性将继承于创建的线程,attr中设置的调度属性将被忽略。
          • PTHREAD_EXPLICIT_SCHED:调度属性将被设置为attr中指定的属性值。
        • policy:可选值为线程的三种调度策略
          • SCHED_OTHER
          • SCHED_FIFO
          • SCHED_RR
      int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
      int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
      
      int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
      int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
      

    7.3.5 线程优先级

    • SCHED_OTHER调度时
      • 静态优先级必须置为 0
      • 处于静态优先级为 0 的线程按照动态优先级被调度
      • 动态优先级值起始于 nice 值,且当前线程处于就绪态并被调度器无视时,其动态值++,以保证竞争CPU的公平性
    • 线程优先级的设置(静态优先级)(SCHED_FIFO和SCHED_RR)
      • 通过以下函数来获得线程可以设置的最高和最低优先级(不支持SCHED_OTHER)
      int sched_get_priority_max(int policy);
      int sched_get_priority_min(int policy);
      
      • 通过以下两个函数来设置和获取优先级
      int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
      int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
      
    • 线程优先级特点
      • 新线程的优先级默认为 0
      • 新线程不继承父线程调度优先级(PTHREAD_EXPLICIT_SCHED)
      • 当线程的调度策略为SCHED_OTHER时,不允许修改线程优先级,仅当调度策略为实时(即SCHED_FIFO或SCHED_RR)时才有效, 并可以在运行时通过pthread_setschedparam()函数来改变,默认为0。

    7.3.6 线程栈

    • 线程栈:
      • 用于存放函数形参、局部变量、线程切换现场寄存器等数据。
      • 使用的是进程的地址空间,默认线程栈大小是 1M
      • 设置和获取线程大小可以使用一下函数
        int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
        int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
        

    7.4 退出线程

    • 线程退出使用 void pthread_exit(void *retval); 函数
    • 注意:不能使用 exit() 函数退出,因为该函数是直接退出进程的,会把进程内所有函数都退出。

    参考

      * 野火
  • 相关阅读:
    Java框架介绍-13个不容错过的框架项目
    微信公众号 模板消息开发
    微信授权-授权方式、公众号是否关注
    Java Spring-Spring与Quartz整合
    Java框架搭建-Maven、Mybatis、Spring MVC整合搭建
    IOS UIView 04- 自定义控件
    IOS UIView 03- 自定义 Collection View 布局
    IOS UIView 02- 深入理解 Scroll Views
    MVC架构中的Repository模式 个人理解
    零开始的领域驱动设计
  • 原文地址:https://www.cnblogs.com/lizhuming/p/14231490.html
Copyright © 2011-2022 走看看