美文网首页
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