第 30 章 线程:线程同步 (Threads: Thread Synchronization)

      +

      核心结论

      • mutex 提供对共享资源的互斥访问:locked/unlocked 两态;lock unlock 必须成对;同一线程重复 lock 默认死锁(Linux NORMAL mutex);unlock 未持有的 mutex 结果未定义;性能优——无竞争时纯用户态 futex(无系统调用)。

      • mutex 类型:NORMAL / ERRORCHECK / RECURSIVE / DEFAULT:NORMAL 不检查错误(Linux 默认);ERRORCHECK 返回错误码(调试用);RECURSIVE 维护 lock count(同线程可多次 lock);DEFAULT SUSv3 未定义(Linux 等同 NORMAL)。

      • 死锁与避免:多个 mutex 锁顺序不一致是经典死锁源;解决方案——mutex 层次(按固定顺序 lock)或 try-and-backoff(trylock + 失败全释放)。

      • 条件变量用于「状态变化通知」:必须与 mutex 配对使用;wait 步骤原子地 unlock mutex + 阻塞 + 被 signal 后 relock;signal/broadcast 区别——signal 至少唤醒一个,broadcast 唤醒全部。

      • pthread_cond_wait 必须放在 while 循环内而非 if:原因——其他线程先 wake、loose predicate 设计、spurious wake-ups(SUSv3 允许);signal 可在 unlock 之前或之后(unlock 后 signal 可能性能更优——wait morphing)。

      • 线程池/join any 模式:pthread_join 只针对特定 TID(无 join any);用 mutex + cond 模拟——线程终止时 signal 条件变量,主线程 while loop 检查 + 扫描所有 TID + pthread_join。

      本章主旨

      本章深入 POSIX 线程同步的两大基石——mutex 与条件变量。读者应掌握:mutex 静态/动态初始化、lock/unlock/trylock/timedlock、4 种 mutex 类型、死锁避免;条件变量 signal/broadcast/wait/timedwait、为何必须用 while 而非 if、condition variable + mutex 模拟 join any(thread_multijoin 模式)。理解「condition variable 无状态,只是一个通知机制」是避免常见错误的关键——signal 在没有 waiter 时会丢失。这是写线程池、生产者-消费者、读写锁(用 mutex+cond 实现)等并发模式的基础。

      一、核心概念

      本章围绕 6 个核心概念展开:从 mutex 基础入手,到 mutex 类型与死锁、动态初始化、条件变量机制、wait 循环模式,最后到 join any 模式。

      概念 定义 + 重要性 实现提示

      mutex 基础

      pthread_mutex_t 类型;两态(locked/unlocked);locked 时其他线程 lock 阻塞;owner-only unlock;静态 PTHREAD_MUTEX_INITIALIZER / 动态 pthread_mutex_init;advisory(非强制)——所有线程必须配合。

      §30.1;静态初始化仅默认属性;动态用于堆/栈 mutex 或自定义属性;图 30-2 展示临界区保护;mutex 操作必须作用于原始 mutex(非副本)。

      mutex 性能与 futex

      无竞争时 mutex lock/unlock 用原子机器指令(无系统调用);有竞争时通过 futex 系统调用进入内核;NPTL mutex 实现基于 futex;锁/解锁 < 10x 自增操作成本。

      §30.1.3;fcntl 锁 44s、System V semaphore 28s(20M 次)对比 mutex 几秒;futex 是「fast user space mutex」;详见 [Drepper, 2004(a)]。

      mutex 类型与错误检查

      PTHREAD_MUTEX_NORMAL:不检查错误——重复 lock 死锁、unlock 不持有 mutex 结果未定义(Linux 上 unlock 成功);PTHREAD_MUTEX_ERRORCHECK:所有错误返回错误码(调试用,慢);PTHREAD_MUTEX_RECURSIVE:维护 lock count(递归锁);PTHREAD_MUTEX_DEFAULT:SUSv3 未定义(Linux 等同 NORMAL)。

      §30.1.7;Listing 30-3 演示 settype;Linux 非标静态初始化 PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP(不可移植)。

      死锁与 mutex 层次

      多线程多 mutex 时按不一致顺序 lock → 死锁;解决——mutex 层次(所有线程按相同顺序 lock);try-and-backoff(trylock + 失败全释放 + 重试)——效率低但灵活。

      §30.1.4;图 30-3;典型场景——A lock mtx1 等 mtx2,B lock mtx2 等 mtx1;always lock mtx1 first 即可避免。

      条件变量与 mutex 配对

      pthread_cond_t 类型;必须与 mutex 配对(所有并发 waiter 用同一 mutex);wait 步骤原子地 unlock + block + relock;signal/broadcast——signal 至少唤醒一个、broadcast 全部;signal 在 unlock 之后调用(wait morphing 优化)。

      §30.2.2;PTHREAD_COND_INITIALIZER 静态初始化;pthread_cond_init 动态;wait 必须 while 循环包裹。

      join any 模式(条件变量模拟)

      pthread_join 只针对特定 TID;用 mutex + cond 模拟「join 任意已终止线程」——线程终止时 state=TS_TERMINATED + signal threadDied;主线程 while numLive>0:wait + 扫描所有 TID + join 已终止的。

      §30.2.4;Listing 30-4 thread_multijoin.c;状态机 TS_ALIVE → TS_TERMINATED → TS_JOINED;numLive/numUnjoined 计数;thread array 跟踪。

      二、详细笔记

      30.1 mutex 基础

      Whatpthread_mutex_t 类型 mutex;两态 locked/unlocked;lock/unlock 操作;静态或动态初始化;advisory 同步机制。

      Why:防止多线程同时修改共享变量;critical section 的原子性保证;现代 NPTL mutex 用 futex 实现,无竞争时无系统调用。

      How

      mutex 三态语义(§30.1):

      • unlocked → 任何线程 lock 立即成功。

      • locked by thread A → 其他线程 lock 阻塞直到 unlock。

      • 同一线程重复 lock 默认死锁(Linux NORMAL mutex)。

      • unlock 不是 owner 的 mutex 结果未定义。

      • unlock 未 locked 的 mutex 结果未定义。

      API(§30.1.1-§30.1.2):

      // 摘自《The Linux Programming Interface》第 30 章
      #include <pthread.h>
      pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;   /* 静态初始化 */
      int pthread_mutex_lock(pthread_mutex_t *mutex);
      int pthread_mutex_unlock(pthread_mutex_t *mutex);
      int pthread_mutex_trylock(pthread_mutex_t *mutex);   /* 失败 EBUSY */
      int pthread_mutex_timedlock(pthread_mutex_t *mutex,
                                  const struct timespec *abstime);  /* 超时 ETIMEDOUT */
      int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
      int pthread_mutex_destroy(pthread_mutex_t *mutex);

      要点:

      • PTHREAD_MUTEX_INITIALIZER 仅用于静态 + 默认属性。

      • mutex 操作必须作用于原始 mutex——SUSv3 规定 mutex 副本操作结果未定义。

      • 每线程用 mutex 协议访问共享资源:lock → 访问 → unlock(critical section)。

      • 多线程 try-lock 可能 starvation——>lock 阻塞队列中线程持续获取锁(pseudocode 演示)。

      • mutex 是 advisory——线程可自由忽略,但正确同步要求所有线程配合。

      性能(§30.1.3):

      • 无竞争 lock/unlock 用原子机器指令——无系统调用。

      • 有竞争时通过 futex 系统调用进入内核。

      • 对比:fcntl 锁 20M 次 = 44s;SysV 信号量 28s;mutex 几秒(同一 x86-32 Linux 2.6.31)。

      • futex = Fast Userspace muTEX;详见 [Drepper, 2004(a)] 与 [Franke et al., 2002]。

      When:保护所有共享可变状态——全局变量、堆对象、共享内存区;持锁时间尽可能短;lock 顺序需明确避免死锁。

      Example

      // 摘自《The Linux Programming Interface》第 30 章 — Listing 30-2
      // 摘自 threads/thread_incr_mutex.c
      static int glob = 0;
      static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
      static void *threadFunc(void *arg) {
          int loops = *((int *) arg);
          int loc, j, s;
          for (j = 0; j < loops; j++) {
              s = pthread_mutex_lock(&mtx);
              if (s != 0) errExitEN(s, "pthread_mutex_lock");
              loc = glob; loc++; glob = loc;
              s = pthread_mutex_unlock(&mtx);
              if (s != 0) errExitEN(s, "pthread_mutex_unlock");
          }
          return NULL;
      }

      30.1.7 mutex 类型

      What:4 种 mutex 类型——NORMAL/ERRORCHECK/RECURSIVE/DEFAULT;决定错误检查策略和递归 lock 行为。

      Why:选择 mutex 类型平衡「性能」与「调试友好度」;递归锁用于需要递归函数的场景;错误检查 mutex 用于开发期发现 bug。

      How

      类型对比(§30.1.7):

      类型 重复 lock 行为 unlock 错误行为

      PTHREAD_MUTEX_NORMAL

      死锁(默认)

      未定义(Linux 上 unlock 成功)

      PTHREAD_MUTEX_ERRORCHECK

      返回 EDEADLK

      返回 EPERM

      PTHREAD_MUTEX_RECURSIVE

      计数 +1

      返回 EPERM

      PTHREAD_MUTEX_DEFAULT

      未定义(SUSv3)

      未定义(Linux 等同 NORMAL)

      设置类型(§30.1.7):

      // 摘自《The Linux Programming Interface》第 30 章 — Listing 30-3
      pthread_mutex_t mtx;
      pthread_mutexattr_t mtxAttr;
      int s;
      s = pthread_mutexattr_init(&mtxAttr);
      s = pthread_mutexattr_settype(&mtxAttr, PTHREAD_MUTEX_ERRORCHECK);
      s = pthread_mutex_init(&mtx, &mtxAttr);
      s = pthread_mutexattr_destroy(&mtxAttr);

      RECURSIVE mutex 语义(§30.1.7):

      • 首次 lock:count = 1;线程成为 owner。

      • 同线程再次 lock:count++。

      • 同线程 unlock:count--;count = 0 时释放。

      • 其他线程 unlock:返回 EPERM。

      • 适用场景:递归函数、回调中需 lock 同 mutex 的库。

      Linux 非标静态初始化(§30.1.7):

      • PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 等——可移植代码避免使用。

      When:默认用 NORMAL(无开销);开发期用 ERRORCHECK(找 bug);递归函数/库需要 RECURSIVE;PTHREAD_MUTEX_DEFAULT 是 PTHREAD_MUTEX_INITIALIZER 默认值。

      Example

      // 摘自《The Linux Programming Interface》第 30 章 — 递归 mutex 使用
      pthread_mutex_t mtx = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;  /* Linux 非标 */
      void recursive_helper(int depth) {
          pthread_mutex_lock(&mtx);
          /* 访问共享资源 */
          if (depth > 0) recursive_helper(depth - 1);
          pthread_mutex_unlock(&mtx);
      }

      30.1.4 死锁

      What:两个或多个线程互相等待对方持有的锁——永久阻塞。

      Why:理解死锁成因是设计多 mutex 程序的关键;mutex 层次是最简单可靠的解决方案。

      How

      经典死锁场景(§30.1.4,图 30-3):

      • Thread A: lock(mutex1) → lock(mutex2) blocks。

      • Thread B: lock(mutex2) → lock(mutex1) blocks。

      mutex 层次方案:

      • 强制所有线程按相同顺序 lock 多个 mutex——例如总是先 mutex1 后 mutex2。

      • 即使无逻辑层次,也可指定任意层次(如按 mutex 地址排序)。

      • 简单、可靠、零开销——推荐作为首选。

      try-and-backoff 方案:

      • 第一个 mutex 用 lock;后续用 trylock。

      • 任一 trylock 失败 → 释放所有 mutex → 重试(可能 sleep 一段)。

      • 优点:无需固定层次。

      • 缺点:可能多次重试;starvation 风险;不高效。

      • 见 [Butenhof, 1996]。

      When:写多 mutex 代码时——确定 mutex 层次;lock 顺序不一致时考虑重构;不要在持锁时调用外部函数(可能间接 lock)。

      Example(mutex 层次):

      // 摘自《The Linux Programming Interface》第 30 章
      /* 全局约定:先 lock mtx_a 再 lock mtx_b */
      void func_a(void) {
          pthread_mutex_lock(&mtx_a);
          pthread_mutex_lock(&mtx_b);
          /* ... */
          pthread_mutex_unlock(&mtx_b);
          pthread_mutex_unlock(&mtx_a);
      }
      void func_b(void) {
          /* 必须按相同顺序 */
          pthread_mutex_lock(&mtx_a);
          pthread_mutex_lock(&mtx_b);
          /* ... */
          pthread_mutex_unlock(&mtx_b);
          pthread_mutex_unlock(&mtx_a);
      }

      30.1.5 动态 mutex 初始化

      Whatpthread_mutex_init/destroy 用于动态/栈 mutex 或自定义属性 mutex。

      Why:某些场景不能用静态初始化——堆上 mutex、栈 mutex、非默认属性 mutex。

      How

      需要动态初始化的场景(§30.1.5):

      • mutex 动态分配在堆上(如链表节点内嵌 mutex)。

      • mutex 是栈上的自动变量。

      • 静态 mutex 但用非默认属性(如 ERRORCHECK 类型)。

      销毁(§30.1.5):

      • pthread_mutex_destroy 释放 mutex 资源;不必 destroy 静态初始化 mutex。

      • 必须 unlocked 时 destroy;之后不能 lock。

      • 动态内存中的 mutex:free 内存前先 destroy。

      • 栈 mutex:宿主函数返回前 destroy。

      • destroy 后可再次 init 复用。

      When:嵌入到动态分配结构中的 mutex(如 hash 表每个 bucket 一 mutex);需要错误检查时用动态 init + settype。

      Example

      // 摘自《The Linux Programming Interface》第 30 章 — 堆中 mutex
      struct node {
          int value;
          pthread_mutex_t mtx;
          struct node *next;
      };
      struct node *n = malloc(sizeof(*n));
      if (n == NULL) abort();
      pthread_mutex_init(&n->mtx, NULL);   /* 默认属性 */
      /* ... 使用 n->mtx ... */
      pthread_mutex_destroy(&n->mtx);
      free(n);

      30.2 条件变量

      Whatpthread_cond_t 类型;用于「状态变化通知」;与 mutex 配对使用;signal/broadcast 通知,wait 阻塞直到通知。

      Why:解决「轮询浪费 CPU」问题——mutex 保护共享变量,cond 通知状态变化;生产者-消费者、join any 都基于此。

      How

      API(§30.2.2):

      // 摘自《The Linux Programming Interface》第 30 章
      #include <pthread.h>
      pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
      int pthread_cond_signal(pthread_cond_t *cond);     /* 至少唤醒一个 waiter */
      int pthread_cond_broadcast(pthread_cond_t *cond);  /* 唤醒全部 waiter */
      int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
      int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
                                 const struct timespec *abstime);

      pthread_cond_wait 步骤(§30.2.2):

      1. unlock mutex(释放以便其他线程修改共享变量)。

      2. 阻塞调用线程直到 cond 被 signal。

      3. 被 wake 后 relock mutex。

      4. 步骤 1-2 原子——其他线程不可能在 unlock 后、block 前 signal。

      signal vs broadcast(§30.2.2):

      • signal:至少唤醒一个 waiter;若多个等待,唤醒哪个未定义。

      • broadcast:唤醒所有 waiter;处理 spurious/redundant wake-up 后回到 wait。

      • 只在「所有等待线程执行相同任务」时用 signal;否则用 broadcast。

      • signal 可能引起「thundering herd」——所有等待线程被唤醒但只有一个能继续。

      signal 与 unlock 顺序(§30.2.2):

      • SUSv3 允许两种顺序:先 unlock 后 signal / 先 signal 后 unlock。

      • 推荐 unlock 后 signal——某些实现用 wait morphing 避免多余 context switch。

      • 若先 signal 再 unlock:waiter 立即 wake,但 mutex 仍 locked → 立即重新 sleep → 多余 context switch。

      condition variable 性质(§30.2.2):

      • 无状态——纯通知机制。

      • signal 时无 waiter → signal 丢失。

      • 新 waiter 不会被旧 signal 唤醒——只 wake 后续 signal。

      When:生产者-消费者模式、线程池任务派发、join any 模拟、读写等待、状态机同步。

      Example(生产者-消费者):

      // 摘自《The Linux Programming Interface》第 30 章 — prod_condvar.c 简化
      static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
      static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
      static int avail = 0;
      /* 生产者 */
      pthread_mutex_lock(&mtx);
      avail++;
      pthread_mutex_unlock(&mtx);
      pthread_cond_signal(&cond);   /* 推荐 unlock 后 signal */
      /* 消费者 */
      pthread_mutex_lock(&mtx);
      while (avail == 0)             /* 必须 while 而非 if */
          pthread_cond_wait(&cond, &mtx);
      /* 现在 avail > 0,处理 */
      avail--;
      pthread_mutex_unlock(&mtx);

      30.2.3 为何 pthread_cond_wait 必须在 while 循环内

      What:wait 返回后必须重新检查 predicate(共享变量状态)——可能不满足预期;必须用 while 而非 if

      Why:SUSv3 允许 wait 虚假 wake-up + signal 设计为「loose predicate」;这是 Pthreads 编程的最常见陷阱之一。

      How

      必须 while 而非 if 的原因(§30.2.3):

      1. 其他线程可能先 wake:mutex 关联的 cond 可能有多个 waiter;即使 signal 线程设置 predicate 到期望状态,其他 waiter 之一可能先获取 mutex 并改变 predicate。

      2. Loose predicate 设计:signal 可设计为「可能有事可做」而非「有事可做」——waiter 必须自己验证。

      3. Spurious wake-up:SUSv3 允许某些实现下 wait 在无 signal 时也返回——多处理器系统的实现细节。

      while 模式(§30.2.3):

      // 摘自《The Linux Programming Interface》第 30 章
      pthread_mutex_lock(&mtx);
      while (predicate_not_satisfied)
          pthread_cond_wait(&cond, &mtx);
      /* 此时 predicate 满足 */
      process_shared_resource();
      pthread_mutex_unlock(&mtx);

      When:任何 pthread_cond_wait 都必须包在 while 循环内;这是硬性规则,没有例外。

      Example

      // 摘自《The Linux Programming Interface》第 30 章 — 错误 vs 正确
      /* 错误:spurious wake-up 可能破坏逻辑 */
      if (avail == 0)
          pthread_cond_wait(&cond, &mtx);   /* 醒来后不检查 avail */
      consume(avail);                        /* avail 可能仍为 0 */
      
      /* 正确:while 循环重新检查 */
      while (avail == 0)
          pthread_cond_wait(&cond, &mtx);
      consume(avail);

      30.2.4 join any 模式(thread_multijoin)

      What:用 mutex + cond 模拟「pthread_join any」——线程池回收任一终止线程;维护全局 thread array + 状态机 + 计数器。

      Why:pthread_join 只针对特定 TID——worker pool 需要回收任一完成线程;条件变量 + mutex 是经典解决方案。

      How

      状态机(§30.2.4,Listing 30-4):

      状态 含义

      TS_ALIVE

      线程在运行

      TS_TERMINATED

      线程已终止但未 join

      TS_JOINED

      线程已 join(state 仅作历史记录)

      全局变量:

      • totThreads:创建线程总数。

      • numLive:存活 + 已终止未 join 的线程数。

      • numUnjoined:已终止但未 join 的线程数。

      • thread[]:每线程记录 tid + state + sleepTime。

      线程终止时(§30.2.4):

      • sleep(work_time)。

      • pthread_mutex_lock(&threadMutex)。

      • numUnjoined++;thread[idx].state = TS_TERMINATED。

      • pthread_mutex_unlock(&threadMutex)。

      • pthread_cond_signal(&threadDied)。

      • return NULL。

      主线程回收循环(§30.2.4):

      // 摘自《The Linux Programming Interface》第 30 章 — Listing 30-4 简化
      // 摘自 threads/thread_multijoin.c
      while (numLive > 0) {
          pthread_mutex_lock(&threadMutex);
          while (numUnjoined == 0)
              pthread_cond_wait(&threadDied, &threadMutex);
          for (idx = 0; idx < totThreads; idx++) {
              if (thread[idx].state == TS_TERMINATED) {
                  pthread_join(thread[idx].tid, NULL);
                  thread[idx].state = TS_JOINED;
                  numLive--;
                  numUnjoined--;
                  printf("Reaped thread %d (numLive=%d)\n", idx, numLive);
              }
          }
          pthread_mutex_unlock(&threadMutex);
      }

      要点:

      • wait 在 while numUnjoined==0 内——确保有线程可 join 时才退出 wait。

      • pthread_join 在 mutex 持锁时调用——Pthread 函数允许(不违反 mutex 规则)。

      • 扫描整个 array——确保 join 所有已 terminate 的线程。

      • numLive==0 时退出——所有线程回收完毕。

      When:线程池实现、worker 模式、批量创建并等待所有线程完成。

      Example:见上述代码。

      三、关键图表

      mutex 类型行为对照表
      类型 重复 lock unlock 错误

      PTHREAD_MUTEX_NORMAL (Linux 默认)

      死锁

      未定义(Linux 上 unlock 成功)

      PTHREAD_MUTEX_ERRORCHECK

      返回 EDEADLK

      返回 EPERM

      PTHREAD_MUTEX_RECURSIVE

      lock count++

      返回 EPERM

      PTHREAD_MUTEX_DEFAULT (SUSv3)

      未定义

      未定义

      性能:NORMAL > RECURSIVE > ERRORCHECK(开销递增)。

      条件变量 wait 步骤详解
      步骤 操作

      1

      调用线程已 lock mutex(前置条件)

      2

      检查 predicate(while 循环内)

      3

      pthread_cond_wait 原子地:unlock mutex + 阻塞当前线程 + 加入 cond wait 队列

      4

      被 signal/broadcast/spurious wake 唤醒

      5

      pthread_cond_wait 自动 relock mutex

      6

      返回后 while 循环重新检查 predicate

      7

      predicate 满足 → 处理共享资源 → unlock

      四、思维导图

      mindmap
        root((第 30 章 线程同步))
          mutex 基础
            pthread mutex t
            locked unlocked 两态
            owner only unlock
            advisory 同步
            临界区保护
            MUTEX INITIALIZER
          mutex 性能
            无竞争 原子指令
            有竞争 futex 系统调用
            fast userspace mutex
            比 fcntl sem 快 10x
          mutex 类型
            NORMAL 默认 Linux
            ERRORCHECK 调试用
            RECURSIVE 递归锁
            DEFAULT SUSv3 未定义
            settype pthread mutexattr t
          死锁
            多 mutex 不一致顺序
            mutex 层次 首选
            try and backoff 备选
            trylock 失败全释放重试
          动态 mutex
            堆上 mutex
            栈 mutex
            非默认属性
            destroy unlocked
            destroy 后可 reinit
          条件变量
            pthread cond t
            必须配 mutex
            signal 唤醒一个
            broadcast 唤醒全部
            wait unlock block relock
            signal 丢失 无状态
            推荐 unlock 后 signal
          wait while 循环
            其他线程先 wake
            loose predicate
            spurious wake up
            SUSv3 允许
            硬性规则无例外
          join any 模式
            thread_multijoin
            TS ALIVE TERMINATED JOINED
            numLive numUnjoined
            wait threadDied
            扫描 array 回收
            线程池基础

      五、重点与易错点

      1. mutex 是 advisory(非强制)——线程可自由忽略;但正确同步要求所有线程配合。

      2. mutex 操作必须作用于原始 mutex——SUSv3 规定 mutex 副本操作结果未定义;不能 memcpy mutex。

      3. 每线程用 mutex 协议访问共享资源:lock → 访问 → unlock;持锁时间尽可能短(避免阻塞其他线程)。

      4. 静态初始化 PTHREAD_MUTEX_INITIALIZER 仅默认属性——动态分配 mutex 或非默认属性需 pthread_mutex_init。

      5. mutex 类型决定错误检查行为:NORMAL 重复 lock 死锁;RECURSIVE 维护 lock count(递归函数/库);ERRORCHECK 调试期发现 bug。

      6. 死锁简单方案:mutex 层次——所有线程按相同顺序 lock 多个 mutex;推荐作为首选策略。

      7. try-and-backoff 适用于无固定层次场景——trylock + 失败全释放 + 重试;代价是效率低。

      8. Linux mutex 基于 futex——无竞争无系统调用;有竞争才用 futex();性能远优于 fcntl 锁和 SysV 信号量。

      9. 条件变量必须与 mutex 配对——所有并发 waiter 用同一 mutex;SUSv3 规定「cond + 多 mutex」结果未定义。

      10. pthread_cond_wait 原子地 unlock + block + relock——避免「unlock 后但未 block」时其他线程 signal;这是「cond 必须配 mutex」的根因。

      11. pthread_cond_wait 必须包在 while 循环内——不是 if;spurious wake-up + 其他线程先 wake + loose predicate 都要求重新检查;这是 Pthreads 编程硬性规则。

      12. pthread_cond_signal vs broadcast:signal 唤醒至少一个(可能 thundering herd);broadcast 唤醒全部;执行相同任务用 signal,不同任务用 broadcast。

      13. signal 无 waiter 时丢失——cond 无状态,纯通知机制;不能「提前 signal 留待后续 wait」。

      14. 推荐 unlock 后 signal——某些实现的 wait morphing 优化避免多余 context switch。

      15. signal vs unlock 顺序 SUSv3 都允许——但 unlock+signal 是更优实践。

      16. join any 模式 = mutex + cond + 状态机 + 计数器——是线程池回收 worker 的经典模式;pthread_join 无「join any」必须自实现。

      17. pthread_join 可在 mutex 持锁时调用——Pthreads 函数不违反 mutex 规则;只有「持锁时调用可能 lock 同 mutex 的函数」才会死锁。

      18. pthread_cond_timedwait 用绝对时间 abstime——非相对时间;需用 clock_gettime(CLOCK_REALTIME) 计算绝对 deadline;返回值 ETIMEDOUT。

      19. pthread_mutex_timedlock 同样用绝对时间——超时返回 ETIMEDOUT;用得少——持锁时间应短。

      20. pthread_mutex_trylock 失败 EBUSY——轮询用 trylock 可能 starvation;推荐设计「锁等待而非轮询」。

        • 跨章衔接:第 29 章线程基础 → 第 30 章 mutex/cond 同步 → 第 31 章 thread-safety(POSIX 函数可重入性)+ thread-specific data(per-thread 存储);第 32 章线程取消 → cleanup handler 与 cond/cancel interaction;第 33 章线程细节 → 信号、LinuxThreads vs NPTL、pthread_atfork。