• Linux多线程


    0. 线程 vs 进程

    何为线程?线程即轻量级进程,如何理解轻量级这个概念?
    我们知道,Linux的资源分为用户空间资源和内核空间资源:

    • 用户空间资源:用来存放用户自定义的一些数据,用户可直接控制;
    • 内核空间资源:用OS统一调配的资源,用户无权进行控制

    1). 用户空间资源

    mark
    由上图可以看出:

    • 进程
      子进程具备自己独立的用户空间(内容全部复制父进程);
      父子进程不可相互访问对方资源;
    • 线程
      仅申请自己的栈空间,与同进程的其它线程共享内存空间;
      需要注意资源的同步和互斥访问问题

    对不同内存空间的解释:

    • 栈(stack):
      存放程序的局部变量(不包括static修饰的变量);
      后入先出(LIFO),
    • 堆(Heap):
      用于存放进程运行中被动态分配的内存段;
      调用malloc/new()分配,free/delete()释放
    • BSS区:Block Started by Symbol
      属于静态内存分配, 存放程序中为初始化的全局变量
    • 全局变量区
      静态(static)全局变量 和 静态(static)局部变量
    • 代码段:只读
      存放程序的执行代码,有时也存储一些只读的常数变量(如字符串常量等)

    下图是以32位系统为例进行介绍的(系统内核占用高地址1GB内存)
    mark
    (该图转自:C程序(进程)的内存布局)

    2). 内核空间资源

    创建线程时,内核仍旧创建一个新的PCB来标识这个线程;因此从内核角度看,进程和线程时一样的。

    进程是OS管理资源的基本单元,线程时OS系统调用的基本单元

    1. 线程基本操作

    功能 进程 线程
    创建 fork() pthread_create()
    退出 exit pthread_exit()
    等待 wait/waitpid() pthread_join()
    取消 abort() pthread_cancel()
    获取ID getpid() pthread_self()
    调度策略 SCHED_OTHER、SCHED_FIFO、SCHED_RR SCHED_OTHER、SCHED_FIFO、SCHED_RR
    通信机制 管道、消息队列、共享内存、信号、信号量 信号、信号量、互斥锁、读写锁、条件变量

    1). 创建线程 - pthread_create()

    • 作用
      创建新的线程

    • 头文件

        #include <pthread.h>
      
    • 函数原型

        int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)
      
    • 参数

    • thread: 存放线程ID

           typedef unsigned long int pthread_t;
      
    • attr:设置线程属性,一般为NULL

    • start_routine: 线程执行哪段代码

    • arg: 运行函数的参数地址,若需传入多个参数,则需要使用包含这些参数的结构体地址

    • 返回值
      成功:0
      失败:非零值

    关于线程的属性
      线程的属性主要围绕其所能申请的资源,用户能够显示管理的线程的属性主要为栈空间信息,如下结构体:

    typedef struct
    {
        int     detachstate;     //线程的分离状态
        int     schedpolicy;     //线程调度策略
        struct  sched_param schedparam;   //线程的调度参数
        int     inheritsched;    //线程的继承性
        int     scope;           //线程的作用域
        size_t  guardsize;       //线程栈末尾的警戒缓冲区大小
        int     stackaddr_set;
        void *  stackaddr;       //线程栈的位置
        size_t  stacksize;       //线程栈的大小
    }pthread_attr_t;
    

    详细的属性操作请参考:http://blog.csdn.net/scanery/article/details/7242768

    2). 获取线程id - pthread_self()/pthread_gettid()

    • 进的pid: 每个进程都有自己独一无二的pid_t pid, 由getpid()函数获得;
    • 线程id
    • 用户空间
      pthread_t pid,由ptread_self()函数获得;该id由线程库维护,其id空间是相对独立的(即不同进程的子线程pid有可能相同);实际上,该id是当前线程的description地址;
    • 内核空间
      从内核中看,每个线程都具有自己独一无二的tid(但是不能通过ps看,也不会在/proc下生成对应编号的目录),只能通过Linux的系统调用syscall(SYS_getpid)来获取

    ①. pthread_self()

    • 作用
      获取调用者的线程ID:pthread_id

    • 头文件

        #include <pthread.h>
      
    • 函数原型

        pthread_t pthread_self(void)
      
    • 参数

    • 返回值
      成功: 线程ID,同pthread_creat()的pid相同
      失败:总是成功

    ②. syscall()

    • 作用
      调用没有glibc实现的函数,使其可以进入内核态

    • 头文件

        #include <sys/syscall.h>
      
    • 函数原型

        int syscall(int number, ...)
      
    • 参数

    • number:
      系统调用号,详细参见 `usr/include/bits/syscall.h>
    • ...:
      number的参数值
    • 返回值
      成功: 根据参数的不同进行返回(一般为0)
      失败:-1

    ③. gettid()

    • 作用
      获取线程的真是tid

    • 头文件

        #include <sys/types.h>
      
    • 函数原型

        pid_t gettid(void)
      
    • 参数

    • 返回值
      成功:0
      失败:无失败

    3). 线程等待/退出 - pthread_join()/pthread_exit()

    ①. pthread_exit()

    • 作用
      退出当前线程

    • 头文件

        #include <pthread.h>      
      
    • 函数原型

         void pthread_exit(void *retval)
      
    • 参数

    • retval:
      退出时线程的状态
    • 返回值
      无返回值

    ②. pthread_join()

    • 作用
      等待子线程结束

    • 头文件

        #include <pthread.h>
      
    • 函数原型

        int pthread_join(pthread_t thread, void **retval)
      
    • 参数

    • thread:
      等待线程的tid
    • retval:
      子线程退出时的返回状态
    • 返回值
      成功: 0
      失败: 非零值

    ③. pthread_cleanup_push()/pthread_cleanup_pop()

    • 作用
      线程退出前执行用户定义的操作

    • 头文件

        #include <phtread.h>
      
    • 函数原型

        void pthread_cleanup_push(void (*routine)(void *), void *arg)  
        void pthread_cleanup_pop(int execute)
      
    • 参数

    • (void (routine)(void)): 压入栈顶的函数
    • arg:压入函数的参数
    • execute: 弹出某函数时是否执行,0为不执行,非零为执行
    • 返回值

    4). 取消线程

    ①. pthread_cancel()

    • 作用
      取消正在执行的线程

    • 头文件

        #include <phtread.h>
      
    • 函数原型

        int pthread_cancel(pthread_t thread)
      
    • 参数

    • thred: 欲取消的线程ID
    • 返回值
      成功: 0
      失败:非0

    ② pthread_setcancelstate() / pthread_setcaneltype()

    • 作用
      取消正在执行的线程

    • 头文件

        #include <phtread.h>
      
    • 函数原型

        int pthread_setcancelstate(int state, int *oldstate)
        int pthread_setcanceltype(int type, int *oldtype)
      
    • 参数

    • state: 欲设置的线程状态
    • oldstate: 存储原来的线程状态
    线程状态 说明
    PTHREAD_CANCEL_ENABLE 默认值,该线程可被取消
    PTHREAD_CANCEL_DISABLE 不可被取消
    • type: 欲设置的类型
    • oldtype: 存储原来的类型
    线程类型 说明
    PTHREAD_CANCEL_DEFERRED 在取消点取消
    PTHREAD_CANCEL_ASYNCHRONOUS 可随时执行新的或未决的请求
    • 返回值
      成功: 0
      失败:非0
  • 相关阅读:
    【C语言】找出1000以内所有的素数
    【C语言】字符数组,碎碎念
    【C语言】将输入的10个数排序
    C语言 排序算法
    冒死透露!全球前25名最臭名昭着的黑客人物
    苹果系统新致命漏洞,黑客可以随意控制您的手机设备
    物流行业的5大安全风险
    黑客来势汹汹,数据科学能拯救社交媒体吗?
    Facebook超过1亿用户数据泄露,疑与中国黑客组织有关?
    太可怕了!黑客可以通过监控智能手机传感器窃取您的密码
  • 原文地址:https://www.cnblogs.com/Jimmy1988/p/7814539.html
Copyright © 2020-2023  润新知