美文网首页
chapter2: 线程 - Programming with

chapter2: 线程 - Programming with

作者: my_passion | 来源:发表于2022-05-17 21:35 被阅读0次

chapter2 线程

2.1 线程 create / use

    pthread_t thread;
    
    int pthread_equal (pthread_t tl, pthread_t t2);
        比较两个 线程标识符 是否 相等 <=> `引用 同一线程
        
        return 
            0   : 是
            else: 
        
    int pthread_create (pthread_t *thread,              // 线程标识符 ptr
                        const pthread_attr_t *attr,
                        void *(*startRoutine)(void *),  // 线程函数 (形参/返回值: void* 类型) 地址 
                        void *arg);                     // 线程函数 的 实参 (arg: void* 类型)
         
    pthread_t pthread_self (void);
        线程 获取 自己的标识符
        
    int sched_yield (void);
    
    int pthread_exit (void *retValue);
        Note
            1] 线程 可 终止自身 
            
            2] 主线程 调 pthread_exit
                进程 继续, 直到所有线程都终止

    int pthread_detach (pthread_t thread);
    
    int pthread_join   (pthread_t thread, 
                        void **retValue); // 线程函数 returnValue( void* ) 的 指针

(1) 线程标识符 ( pthread_t 类型 )
    
    [1] 作用 
    
        表示线程
            1] create 线程
            2] `操作 线程`
                
    [2] 存储 (大多情况, 存到 [2] [3] )
        
        ————————————————————————————————————————————
        1] stack 自动存储 
            
            2 种 case 
                
                1> 线程标识符 只在 `线程函数内 使用`
                
                2> 线程完成前, `线程函数 不会返回`
        ————————————————————————————————————————————    
        2] heap
        ————————————————————————————————————————————
        3] 共享( static / extern ) 变量
        ————————————————————————————————————————————
        
(2) 线程函数
    
    paraType / returnType 都是 void*
            
    `最佳实践` 
        任何 线程函数 都 返回 值(即使只是 null) 是个好主意 
    
(3) 初始线程(主线程 main) is special
    
    原因: main 返回时, 进程 终止 & "线程蒸发" - 没有理由清理 (其状态 / 可能完成的任何事情)
    
        "线程蒸发" 有时很有用

        但, 大多数情况下, 进程 比 进程中的线程 lifetime 长

        Note
            若 `不等待 线程`, 就应该确保 `线程先于 其 所属进程 结束`  
                
    1) 若 main 线程 将其 线程标识符 存储在 另一线程可访问的地方
            
            则, 该线程 可 join / detach main 线程
            
(4) 线程 分离/detach 
    
    1) 目的
    
        [1] 告诉 OS 
        
            你不再需要该线程
            
            允许 OS `回收 ( 分配给线程的 ) 资源`
        
        [2] 确保 `已终止的线程` 使用的 `资源` 对进程可用

            thread 应 始终 在完成后 `分离`

            `已终止 但 未分离` 的 线程
                
                maybe 保留虚拟内存 (堆栈 + 其他系统资源)
            
    2) 4 种 case 
        
        [1] 创建时 detach: 线程属性 
                
        [2] 自行 detach 
    
        [3] 被 other 线程 detach
            
            线程1 可被 线程 2 (知 1的 pthread_t 即可) 分离

        [4] 被 pthread_join detach 
        
            pthread_join 

            1] 机制 
            
                1> 阻塞 caller, 直到指定线程已终止
                
                2> 调 pthread_join 会 `自动 detach` 指定线程

            2] 何时用 ? 你想知道
            
                1> 线程 返回值

                2> 线程 何时完成
            
            3] Note 
            
                若 省略 return 语句, pthread_join 仍会返回 某些值 
                    线程 启动函数的地方 会 store 返回值(可能是1个 register )
            
            4] 若 pthread_join 的 caller 线程 
                
                1> 不关心 callee 的返回值, 或 
                
                2> 知道 callee 没有返回值 
                
                    => caller 线程 可在调 pthread_join 时, 第2参数 用 NULL
                        而不是 返回值实参(retVal) 的 地址(&retVal)
            
    3) 调 哪个函数实现 ?
        
        pthread_detach
    
            可随时用于 分离线程 
    
    4) 线程 被 detach 后, 就 不能再 join 了 
    
(5) 线程编程模型 优势
    
    `共享地址空间` 提供的 `高效 线程间通信`

        通常, 1组线程 协作以实现共同目标

            每个线程 对 `共享数据流` 执行特定任务 -> 然后将 `数据 传递` 到 next 线程

            管理 线程
            |
            |   1] 控制
            |   2] 划分 work/任务
            |/
            worker thread

(6) 多线程 要知道 某线程 何时结束 的 case 
    
    通知线程 
        |
        |   广播
        |
        cv : 唤醒 多个等待线程
        |\
        |   等待 (在 cv 上)
        |
    多线程 

2.2 线程 lifetime

(0) 线程 4种基本状态: 实现 上, 可细分出 "子状态"

        终止 / terminated 
            退出 / exited
            取消 / cancelled

        阻塞 / blocked 
            阻塞在 
                cv    上
                mutex 上
                read  中
                
    ——————————————————————————————————————————————————————————————————————————————      
    TABLE2.1 线程状态
    ——————————————————————————————————————————————————————————————————————————————
        状态                  含义
    ——————————————————————————————————————————————————————————————————————————————  
    [1] Ready       线程 `能运行`, 但 正 `等待 CPU`      
                    3 cases 
                    1]  刚               start     (启动)
                    2]  刚 被             unblocked (被 解阻塞)
                    3]  刚 被 另一线程    preempted (被 抢占)
    ——————————————————————————————————————————————————————————————————————————————
    [2] Running     线程 当前正在运行
                    多 CPU 上, 1个进程中 可能有 多个 正在运行的线程 
    ——————————————————————————————————————————————————————————————————————————————                  
    [3] Blocked     线程 `不能运行`, 因为它正在 
                        `等待 sth`
                            1]     cv 
                            2]  去 lock a mutex 
                            3]     I/O 操作 to complete
    ——————————————————————————————————————————————————————————————————————————————              
    [4] Terminated  线程 终止方式
        1]  从 startRoutine 返回 
        
        2]  调 pthread_exit + 完成所有 cleanup handlers
                                        |
                                        |
                                    调 pthread_cleanup_push 注册 (register) 线程
                                    还没调 pthread_cleanup_pop 来 删除 线程
                                    
        3]  被 cancelled + 完成所有 cleanup handlers 
        
        4]  没被 detached, 也 没被 joined
        
            一旦被 detached 或 joined, 线程 将会被 回收/recycled
    ——————————————————————————————————————————————————————————————————————————————  

    线程 可能 `阻塞自己` 多次, 等待外部事件
    
    线程 
        1] 若被 `detach`, 终止时会被 立即 `回收 (recycled)`, 而非 销毁(destroyed)
        
            大多数 OS 会 `重用(reuse)` 其(detached 线程的) `资源 来 创建新线程`
            
        2] 没被 detach, 会 `保持在 终止状态`, 直到 joined 或 detached 
                
(1) 创建
        
    ————————————————————————————————————————————————————————————————————————————————    
    线程种类            线程 何时 被创建
    ————————————————————————————————————————————————————————————————————————————————    
    1] 初始线程         创建进程 时
    ————————————————————————————————————————————————————————————————————————————————
    2] 其他线程         Pthreads 系统上, 显式调 pthread_create 时
    ————————————————————————————————————————————————————————————————————————————————
    3] 信号触发的线程  进程 信号通知机制 为 SIGEV_THREAD + 进程接收到 POSIX 信号 时
    ————————————————————————————————————————————————————————————————————————————————
    
    线程被创建时, 状态 为 Ready 
    
    Note    
        创建线程 (从 pthread_create ) 的 返回 
            与 `该新线程 的调度` 之间 
                无 同步 
        
            =>  pthread_create 返回前, 
                    线程 可能已经                                 
                        1] start 
                        2] 完成 + 终止

(2) 启动 / startup

    初始线程 与 other 线程 diff: 4 点
    
    [1] startRoutine 的 调用
            
            main 
                从程序外部
                    UNIX OS
                        crtO.o 初始化进程 -> 调 main
    
    [2] startRoutine 的 传参
        
        main
            参数数组 argc + argv, 
        else    
            void*
            
    [3] 线程 终止 

        1] 从 startRoutine 返回
            
            1)  main 返回 -> 本(初始)线程终止 + 进程立即终止 => (进程中) other 线程 立即 蒸发           
            
            2)  else      -> 本线程终止       + other 线程 继续运行 
        
        2] 调 pthread_exit
        
            main 中 调 pthread_exit -> 初始线程 终止 + other 线程 继续运行
    
    [4] 运行 堆栈 
        
        1] 初始线程 -> 进程堆栈: 可增长到相当大小 
        
        2] else     -> 线程堆栈: 受限 -> 堆栈溢出 -> segmentation fault  / 总线错误 

(3) 运行 / 阻塞 Running / blocking
    
    sleep = Ready + blocked 
    
        ————————————————————————————————————————————————————    
            动作          ->              线程进入的 状态 
        ————————————————————————————————————————————————————    
        1] blocked                          
                |\
                |
            所需 `资源 不可用`
                                                sleep
        2] preempted                        
                |\
                |
            OS 调度 CPU 被分给 other 线程
        ————————————————————————————————————————————————————    
        1] 首次 created           
                    
        2] unblocked                            Ready
        
        3] preempted
        ————————————————————————————————————————————————————

        线程 Ready + 被 处理器选中 去执行      running 
        
            本处理器 
                other 线程 
                    1] blocked 
                    2] preempted 
            多处理器 
                2] 未用的 处理器 选中
        ————————————————————————————————————————————————————        
        线程      
            1] 试图 lock 
                当前已被 locked 的 mutex 
            
            2] 等待 在 cv 上 
                                                blocked 
            3] 尝试 无法立即完成的 I/O 操作
            
            4] 对 当前未挂起 信号 调 sigwait
        
            5] page fault
        ————————————————————————————————————————————————————
        
    lifecycle.c
        [1] main() 调 pthread_join -> main 线程 进入 `blocked 态`, 等待 newly created thread to run
        [2] 若 `此时` 新线程 还没 run -> 新线程 从 `Ready 态` 转到 `running 态`
        [3] 新线程运行完成 并返回时, main 线程 unblocked -> 进入 `Ready 态` -> 等 CPU 可用 -> 进入 `running 态` -> 完成

(4) 终止 / Termination

    1) 若 线程有 non-NULL 线程特定数据
        
        关联 dtor 会被调用
    
    2) 僵尸(zombie) 线程
        
        [1] detached -> 线程终止时 立即进入 recycling(被 OS 回收)
        
        [2] terminated 时 还没被 detached -> 保持 terminated 态 (僵尸 线程)
            
            1] 类 Unix 中 僵尸进程

            2] 弊端 
                保留 其 运行时的 系统资源 
                    
                1] 线程标识符       pthread_t
                2] 线程函数返回值  void*

            3] other 线程 join 僵尸线程: 僵尸线程 被 唤醒 
                     
                pthread_join 
                    
                1> 提取出 其 合适的返回值 

                2> detach 它
                    => 僵尸线程 可能(在 pthread_join 返回前) recycled
                
                3> 返回
        
            => 僵尸线程 的 返回值 不能在 其 堆栈上 -> caller 可能会覆盖
                
    3) 线程 `正常 终止` ( 1] 从 线程启动函数 返回  2] 调 pthread_exit )
    
        与 `因 取消而终止` 的 唯一外部差异是 
            
            取消线程的 返回值 始终是 pthread_cancelled 
                
                => cancelled 不被视为 线程状态 
                    
    4) 线程 返回值 
        
        [1] pthread_join + `返回值 ptr` 传入 
            
        [2] detach 线程 + `返回值( 某种 struct ) ptr` 作 线程函数实参 -> 线程函数在 该 ptr 上写
                            
(5) 回收 / Recycling
    
    1) 线程 detached -> 线程终止时 立即 recycled (被回收)
        
    2) 回收 会 `释放` 系统 或 进程 `资源`
        ————————————————————————————————
        1] 线程 returnValue 的 storage
        ————————————————————————————————
        2] 堆栈
        ————————————————————————————————
        3] register 状态 的 内存
        ————————————————————————————————
        
        其中一些资源 可能已在 线程 terminated 时释放

        Note
                
            线程终止后 不能访问 的 resource 
                线程 堆栈 memory
                    过时(obsolete)

    // ====== thread_lifecycle.c        
    #include <pthread.h>
    #include "errors.h"

    void *thread_routine(void *arg) 
    {
        return arg;
    }

    int main(void) 
    {
        // (1)
        pthread_t threadId;
        void *thread_result;

        // (2)
        pthread_create(&threadId, NULL, thread_routine, NULL);
        
        // (3)
        pthread_join(threadId, &thread_result);

        if (thread_result == NULL)
            return 0; // success
        else
            return 1;
    }

后续章 图

image.png image.png image.png image.png image.png image.png

相关文章

网友评论

      本文标题:chapter2: 线程 - Programming with

      本文链接:https://www.haomeiwen.com/subject/yqdiurtx.html