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;
}
后续章 图
网友评论