%%
# 纲要
> 主干纲要、Hint/线索/路标
- **线程的创建与管理**:创建、启动和管理线程
- **线程间同步**:线程之间如何协作,保护共享数据,避免条件竞争;
- 锁
- 互斥量 `pthread_mutex_t`
- 读写锁 `pthread_rwlock_t`
- 自旋锁 `pthread_spinlock_t`
- 条件变量 `pthread_cond_t`
- 信号量 `sem_t`
- 屏障 `pthread_barrier_t`
- **线程间通信**
- Futures and Promises:用于从异步操作中接收结果。
- 任务队列和工作窃取:何使用任务队列以及如何实现高效的工作分配和负载平衡
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
%%
# POSIX 多线程&同步头文件总结
同步原语(互斥锁、条件变量、信号量)的**原理说明**参见[[02-开发笔记/05-操作系统/并发相关/同步原语|同步原语]] ⭐,**下文直接简要介绍 API**。
POSIX 线程与同步相关的头文件汇总:
| 头文件 | 功能 |
| --------------- | ---------------------------------------------------------------------------------------------------------- |
| `<pthread.h>` | **POSIX 线程库**,提供**线程管理**、**互斥量**、**条件变量**相关 API (`pthread_create`, `pthread_mutex_t`, `pthread_cond_t` 等) |
| `<semaphore.h>` | **POSIX 信号量**,用于线程间同步(`sem_init`, `sem_wait`, `sem_post` 等) |
| `<unist.h>` | 提供**线程休眠**、系统调用等基础功能。 |
> [!quote] C++ 线程 & 同步相关的 API 参见 [[02-开发笔记/01-cpp/多线程并发相关/cpp-多线程&同步|cpp-多线程&同步]]
>
<br><br>
### POSIX 线程库与 C++ 线程库比较
| 库 | **POSIX 线程库** | **C++ 标准线程库** | Boost 线程库 |
| ----------- | ------------------------------------------------- | --------------------------------------- | --------- |
| 头文件 | `<pthread.h>` | `<thread>` | |
| 说明 | **POSIX 标准定义的线程库**,主要用于 C 语言,也支持 C++ | C++标准库的一部分 (since C++11) | |
| **语法** | 底层 API,面向过程的**函数调用**风格。 | **面向对象**风格,结合**RAII**范式 | |
| **线程管理** | **手动管理**线程生命周期,使用`pthread_create`,`pthread_join`等 | 通过 **“线程对象” 自动管理线程的生命周期** | |
| **同步原语** | `pthread_mutex_t`,`pthread_cond_t`等 | `std::mutex`,`std::condition_variable`等 | |
| **错误处理** | 通过**返回值手动处理错误** | **基于 C++异常机制** | |
| **与 C++集成** | 无法直接与 C++特性(如 RAII、异常)集成 | 完全符合 C++特性,如 lambda、RAII 等 | |
| **跨平台支持** | **UNIX-like 系统** | **跨平台支持**,包括 Windows、Linux、macOS | |
> [!example] 创建线程
> ![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-6EF1A38AB19B7D385C7790BCDC4F1509.png|915]]
<br><br>
### POSIX 线程同步 API 中的资源生命周期管理
POSIX API,对互斥量、条件变量、信号量、屏障等同步原语的资源管理,需要**手动初始化 & 释放资源**:
- (1)**资源分配 & 初始化**(部分 API 额外支持静态初始化)
- 静态初始化:适用于 **静态变量**,可利用 "**宏**" 值直接进行初始化;
- 动态初始化:由 `*_init()` 函数进行;
- (2)**资源释放**:
- 由 `*_destroy()` 函数完成
| 变量类型 | 静态初始化宏 | 动态初始化 | 资源释放 |
| ------------------- | ---------------------------- | -------------------------------------- | ------------------------------- |
| `pthread_mutex_t` | `PTHREAD_MUTEX_INITIALIZER` | `pthread_mutex_init(&mtx, nullptr)` | `pthread_mutex_destroy(&mtx)` |
| `pthread_cond_t` | `PTHREAD_COND_INITIALIZER` | `pthread_cond_init(&cv, nullptr)` | `pthread_cond_destroy(&cv)` |
| `pthread_rwlock_t` | `PTHREAD_RWLOCK_INITIALIZER` | `pthread_rwlock_init(&lock, nullptr)` | `pthread_rwlock_destroy(&lock)` |
| `pthread_barrier_t` | ❌ 不支持 | `pthread_barrier_init(&b, nullptr, n)` | `pthread_barrier_destroy(&b)` |
| `sem_t` | ❌ | `sem_init(&sem, 0, n)` | `sem_destroy(&sem)` |
此外,**属性相关的变量** 也均需要 "**手动初始化 & 释放资源**"
| 变量类型 | 初始化 | 销毁 |
| ----------------------- | --------------------------------- | ------------------------------------ |
| `pthread_attr_t` | `pthread_attr_init(&attr)` | `pthread_attr_destroy(&attr)` |
| `pthread_mutexattr_t` | `pthread_mutexattr_init(&attr)` | `pthread_mutexattr_destroy(&attr)` |
| `pthread_rwlockattr_t` | `pthread_rwlockattr_init(&attr)` | `pthread_rwlock_attr_destroy(&attr)` |
| `pthread_condattr_t` | `pthread_condattr_init(&attr)` | `pthread_condattr_destroy(&attr)` |
| `pthread_barrierattr_t` | `pthread_barrierattr_init(&attr)` | `pthread_barrierattr_destroy(&attr)` |
#### 说明示例
示例一:静态初始化 + 资源释放
```cpp
// 静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int main() {
pthread_mutex_lock(&mutex);
// ...
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex); // 释放
return 0;
}
```
示例二:动态初始化 + 资源释放
```cpp
pthread_mutex_t mutex; // 未使用宏进行静态初始化
int main() {
pthread_mutex_init(&mutex, nullptr); // 动态初始化
phtread_mutex_lock(&mutex);
// ...
pthread_mutexunlock(&mutex);
pthread_mutex_destroy(&mutex); // 释放
}
```
<br><br><br>
# POSIX 线程
> 头文件 `<pthread.h>`,API 说明参见[^1]
> [!NOTE] POSIX 进程 API 与线程 API 的比较
>
> ![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-611C25C4054BB1D38BEE39D223E063C4.png|633]]
> ^1o2a91
<br>
## 线程创建、终止、清理
#### pthread_create 线程创建
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-D2CA1E359C688D8170549A909F5E6752.png|713]]
> [!important] POSIX API 下,对**线程函数的定义形式**有明确要求:形如 `void* thread_func(void* arg);`
>
> - 返回值类型:`void*`
> - 可**接收任意类型参数**:在 `pthread_create()` 创建线程时传入,在线程函数内获取该参数(通过强制类型转换)
> - 参数类型:`void*`(唯一)
> - 可**返回任意类型结果**:线程函数内通过 `pthread_exit()` 或 `return` 返回结果,外层线程通过 `pthread_join()` 获取返回值。
>
>
#### pthread_exit 线程退出
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-B191904FF1D68746E89EE896E11BFD95.png|784]]
> [!info] **单个线程**可通过三种方式退出:
>
> 1. 正常 `return` 返回;
> 2. 主动**调用 `pthread_exit()`** 退出;
> 3. 被其他线程 **调用 `pthread_cancel()`** 取消该线程
#### pthread_join 资源回收
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-B3ED39612B4402F180E1DD97324B62DD.png|719]]
线程退出后,系统不会立即释放其占用资源(独立栈空间、线程控制块)等,需由**调用线程通过 `pthread_join()` ==阻塞等待目标线程结束==并回收资源**。
示例:
```c
#include <pthread.h>
#include <stdio.h>
#include <assert.h>
// 线程函数, 以`void*`接收任意类型参数, 以 `void*`返回任意类型参数.
void* threadFunc(void* arg) {
pthread_t tid = pthread_self(); // 获取自身线程id;
printf("Thread[%lu]: %s\n", (unsigned long) tid, (const char*) arg);
return NULL;
}
int main() {
printf("Main:begin\n");
pthread_t p1, p2;
int rc;
// 创建两个线程
rc = pthread_create(&p1, NULL, threadFunc, "A"); assert(rc == 0);
rc = pthread_create(&p2, NULL, threadFunc, "B"); assert(rc == 0);
// join阻塞等待线程完成
rc = pthread_join(p1, NULL); assert(rc == 0);
rc = pthread_join(p2, NULL); assert(rc == 0);
printf("Main:end\n");
return 0;
}
```
<br>
## 线程分离
可通过 "**线程属性**" 设置线程为 "**分离状态**",此后无需由其调用线程通过 `pthread_join()` 等待并回收资源,而系统会在 **==线程终止时==自动回收资源**。
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-FB2101A26F71D5C0C4F71CF6C0D64922.png|812]]
```c
#include <pthread.h>
#include <stdio.h>
void* threadFunc(void* arg) {
pthread_t tid = pthread_self(); // 获取自身线程id;
printf("Thread: %lu\n", (unsigned long) tid);
return NULL;
}
int main() {
pthread_t t1;
pthread_create(&t1, NULL, threadFunc, NULL);
// 分离线程, 该线程终止时由系统自动回收其资源
pthread_detach(t1);
// 无需主线程再调用pthread_join().
}
```
<br>
## 线程函数参数传递、返回值传递
使用示例:
```cpp
#include <pthread.h>
#include <unistd.h> // getpid
#include <cstdio>
#include <cassert>
using namespace std;
void* threadFunc(void* arg) {
pthread_t tid = pthread_self();
int* num = static_cast<int*>(arg);
printf("Thread[%ld]: %d\n", static_cast<unsigned long>(tid), *num);
int* result = new int(*num + 100);
return result; // C++支持任意类型指针转void*.
}
int main() {
pthread_t t1, t2;
int num1 = 1, num2 = 2;
// 通过create传递参数
int err;
err = pthread_create(&t1, nullptr, threadFunc, &num1); assert(err == 0);
err = pthread_create(&t2, nullptr, threadFunc, &num2); assert(err == 0);
// 通过join获取返回值
void *res1, *res2;
pthread_join(t1, &res1); // 第二参数是`void**`类型
pthread_join(t2, &res2);
int *val1 = static_cast<int*>(res1), *val2 = static_cast<int*>(res2);
printf("%d, %d\n", *val1, *val2);
delete val1;
delete val2;
}
```
> [!note] C++ thead API 中,支持**向线程函数传递参数(多个**),但不支持直接返回结果。
>
> 若需要执行异步任务并返回结果,需搭配使用 `std::async()` 与 `std::future<>` 。
<br>
<br><br><br>
# POSIX 锁
## POSIX 互斥锁
> 头文件 `<pthread.h>` ,API 说明参见 [^2]
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-F06478BDF354BF5DD07FED66ABC397F7.png|662]]
使用流程示例:
```C
// 静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 动态初始化
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, nullptr);
// 加锁
pthread_mutex_lock(&mutex); // 阻塞
pthread_mutex_trylock(&mutex); // 非阻塞, 无法获取锁时返回EBUSY
// 释放锁
pthread_mutex_unlock(&mutex);
// 销毁锁
pthread_mutex_destroy(&mutex);
```
<br>
## POSIX 读写锁
> 头文件 `<pthread.h>` ,API 说明参见[^3]
> [!NOTE] "读写锁" 的概念说明参见:[[02-开发笔记/05-操作系统/并发相关/同步原语#读写锁|同步原语#读写锁]]
>
> POSIX 中提供的实现是 "**读优先锁**",即当**读锁**已被持有时,仍可继续请求读锁,而**写锁将一直阻塞**直至所有读锁释放。
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-B8C93B8A1E57C02C930160E059CF0BEE.png|749]]
<br>
## POSIX 自旋锁
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-6B145BA6FA79ABB53CFE8873D384DB84.png|731]]
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-3D0BA182A915149B0FB58B746271C645.png|678]]
<br><br><br>
# POSIX 条件变量
> 头文件 `<pthread.h>` ,API 说明参见[^4]
> [!NOTE] "条件变量" 的概念说明参见:[[02-开发笔记/05-操作系统/并发相关/同步原语#条件变量|同步原语#条件变量]]
### 初始化条件变量
- (1)静态初始化:`pthread_cond_t cond = PTHREAD_COND_INITIALIZER;`
- (2)动态初始化:针对 "**动态分配的条件变量**",需使用 `pthread_cond_init()` 函数动态初始化条件变量,
- 示例:`pthread_cond_t cond = pthread_cond_init(&cond, NULL)`
使用流程示例:
```cpp
// 1) --------静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 2) --------动态初始化
pthread_cond_t cond;
pthread_contattr_t attr;
// 初始化条件变量属性
pthread_condattr_init(&attr);
// 初始化条件变量
pthread_cond_init(&cond, &attr); // 默认属性时, 第二项参数可直接传nullptr.
// 销毁条件变量属性对象
pthread_condattr_destory(&attr);
// 销毁条件变量
pthread_cond_destory(&cond);
```
### 条件变量操作 API
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-C77BE95683AC2EC147AD631BCB982D77.png|675]]
> [!NOTE] 调用上述函数时必须先持有 "互斥锁",因为条件状态本身属于 "共享资源",改变条件状态需受保护
> [!NOTE] `pthread_cond_wait()` 会对加锁的互斥量解锁
>
> 该函数将 **加锁后的互斥量** 作为其第二个参数,其会让调用线程进入"**休眠**"同时 **==释放锁==**(否则**其他线程无法获取锁并将其唤醒**)。当休眠线程被其他线程唤醒而**从 `pthread_cond_wait()` 返回时**,其将**再次获得互斥锁**。
>
> 注:`pthread_cond_wait()` 直到 "**==被唤醒==**" & "**==获得指定锁==**" 后才会返回。
![[02-开发笔记/05-操作系统/并发相关/同步原语#^6hqcq5]]
<br>
### 使用示例
```cpp
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
int done = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 通知线程
void* thr_exit(void* arg) {
int rc;
// 修改共享变量done, 条件变量cond都需要加锁.
rc = pthread_mutex_lock(&mutex); assert(rc == 0);
done = 1;
rc = pthread_cond_signal(&cond); assert(rc == 0);
rc = pthread_mutex_unlock(&mutex); assert(rc == 0);
printf("thr_exit() has been called.\n");
return NULL;
}
// 等待线程
void* thr_join(void* arg) {
int rc;
// 检查共享变量done, 需要加锁.
rc = pthread_mutex_lock(&mutex); assert(rc == 0);
while (done == 0) { // 当done为0时, 线程进入休眠等待状态, 释放互斥锁, 等待被唤醒.
rc = pthread_cond_wait(&cond, &mutex); assert(rc == 0);
}
rc = pthread_mutex_unlock(&mutex); assert(rc == 0);
printf("thr_join() has been called.\n");
return NULL;
}
/*
*/
int main() {
pthread_t p1, p2;
int rc;
rc = pthread_create(&p1, NULL, thr_exit, NULL); assert(rc == 0);
rc = pthread_create(&p2, NULL, thr_join, NULL); assert(rc == 0);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
```
<br><br><br>
# POSIX 信号量
> 头文件 `<semaphore.h> `
> [!NOTE] "信号量" 的概念说明参见:[[02-开发笔记/05-操作系统/并发相关/同步原语#信号量|同步原语#信号量]]
POSIX 信号量有两种类型[^5] [^6]:
- **具名**信号量(文件形式)
- **匿名**信号量(内存变量)
二者均可用于 "**进程间同步**" & "**线程间同步**"。
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-F57BCEE79354B91705F01F0B4A484785.png|352]]
| | 匿名信号量 | 具名信号量 |
| ----- | --------------------------------------------- | ------------------------------ |
| 形式 | `sem_t` 变量 | 文件系统路径名 |
| 创建方式 | `sem_init()` | `sem_open()` |
| 销毁方式 | `sem_destroy()` | `sem_close()` + `sem_unlink()` |
| 进程间共享 | ✔️(需**设置 shared 参数非 0**,并将 `sem_t` 变量至于共享内存区) | ✔️(天然跨进程共享) |
| 持久性 | 存在于内存,进程退出后销毁 | 存在于系统,直至 `sem_unlink` 删除 |
## 信号量操作
###### sem_wait & sem_trywait
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-0DB9771DD1807752591B6D36B894A867.png|616]]
<br>
###### sem_post & sem_getvalue
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-4C0BD8C6F8518AFE36DB19BA596A7C82.png|628]]
<br>
## 匿名信号量
![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-8095DB25F1E5727A5B7A621AB1797B11.png|656]]
匿名信号量主要用于 "**线程间同步**",此时 `sem_init()` 第二参数需为 `0`,表示**不在进程间共享**。
> [!NOTE] 匿名信号量可用于 "线程间同步" & "进程间同步" [^7]
>
> ![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-1D91616F7E6873ED9038F5F221505859.png|432]]
>
使用流程示例:
```c
// 声明信号量
sem_t sem;
// 初始化信号量为指定值
sem_init(&sem, 0, value)
// P: 等待值为正, 并-1;
sem_wait(&sem);
// V: 令值+1, 并唤醒一个等待线程或进程
sem_post(&sem);
// 销毁
sem_destroy(&sem);
```
<br>
## 具名信号量
> 参见 [^7] [^6]:
> ![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-F61EF835F63FB67187428ECF10AF030A.png|608]]
具名信号量适用于 "**进程间同步**":
- `sem_open()`:创建 or 打开一个已存在的**具名信号量**,返回指向该信号量的 `sem_t*` 指针;
- `sem_close()`:关闭当前进程/线程打开的**具名信号量**(仅关闭,而非删除,具名信号量随内核持续)
- `sem_unlink()`: 从内核中**移除指定的具名信号量**(不影响已被打开/引用的信号量,销毁会延迟至最后一个引用结束)
使用流程示例:
```c
int main() {
// 创建具名信号量
sem_t* sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0644, 0);
if (sem == SEM_FAILED) {
perror("sem_open");
return 1;
}
// ...
sem_post(sem);
// ...
sem_wait(sem);
// ...
sem_close(sem); // 关闭引用
sem_unlink(SEM_NAME); // 清除具名信号量(使用路径名)
}
```
<br><br><br>
# POSIX 屏障
> 头文件 `<pthread.h>` ,API 说明参见[^8]
屏障(Barrier)机制允许 **==一组线程==在执行到某个 "同步点" 时均==陷入等待==**,**直至所有合作线程均到达该同步点后,各线程才会继续执行**。
> [!info] 屏障会自动重置并被复用
> 当**指定数量线程**到达同步点,全部通过屏障之后,**屏障会被==自动重置为初始值==**,支持被再次复用。
<br>
## 屏障的用法
1. **声明一个屏障变量**:**`pthread_barrier_t` 类型**
2. **初始化屏障**:指定 "**屏障计数**"——**屏障所需等待的线程数**。
- 调用 `pthread_barrier_init(&barrier, NULL, count)`;
3. **使用屏障**:任一线程**调用 `pthread_barrier_wait()` 而陷入阻塞**,直至**指定的屏障计数满足后**,**所有阻塞线程均被唤醒**。
> [!info] 屏障 API
>
> ![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-820D08243FC40DE9D4497E220548ECDD.png|694]]
>
> ![[_attachment/02-开发笔记/05-操作系统/并发相关/POSIX 线程&同步 API.assets/IMG-POSIX 线程&同步 API-25D384D6D02D113C4B97A8EEDB952E1A.png|676]]
>
>
#### 使用示例
```cpp
#include <pthread.h>
#include <semaphore.h>
// 多个H线程和O线程, 必须同时阻塞直至凑齐一个H2O后三个线程才能分别执行打印.
// 1) 屏障用于等待凑齐3个线程 => 计数值为3的屏障
// 2) 最多只能允许2个H线程接触屏障, 需阻塞多余H线程. => 值为2的信号量
// 3) 最多只能允许1个O线程接触屏障, 需阻塞多余O线程. => 值为1的信号量
class H2O {
public:
H2O() {
pthread_barrier_init(&barrier, nullptr, 3);
sem_init(&sem_h_in, 0, 2); // 只允许2个H接触屏障
sem_init(&sem_o_in, 0, 1); // 只允许1个O接触屏障
}
~H2O() {
pthread_barrier_destroy(&barrier);
sem_destroy(&sem_h_in);
sem_destroy(&sem_o_in);
}
void hydrogen(function<void()> releaseHydrogen) {
sem_wait(&sem_h_in);
pthread_barrier_wait(&barrier);
releaseHydrogen();
sem_post(&sem_h_in); // 唤醒下一个阻塞的h
}
void oxygen(function<void()> releaseOxygen) {
sem_wait(&sem_o_in);
pthread_barrier_wait(&barrier);
releaseOxygen();
sem_post(&sem_o_in); // 唤醒下一个阻塞的o
}
private:
pthread_barrier_t barrier;
sem_t sem_h_in;
sem_t sem_o_in;
};
```
<br><br><br>
# POSIX `pthread_once`
`pthread_once(*init_flag, func)` 函数搭配 `pthread_once_t` 类型的变量 `init_flag`使用:当多个线程均调用了 `pthread_once()` 时,**系统保证仅首次调用有效**,**其余调用会在首次初始化时阻塞**,**初始化完成后的所有再次调用都会直接跳过**。
```c
// 声明once_flag
pthread_once_t init_flag = PTHREAD_ONCE_INIT;
void resource_init(); // 某个共享资源的初始化函数
void* thread_task(void* arg) {
pthread_once(&init_flag, resource_init); //在所有线程中, 仅首次调用有效.
// ...
}
```
<br><br>
# 参考资料
- 《Unix 环境高级编程》
- 第 11 章:线程
- 11.6 线程同步
- 11.6.1 互斥量
- 11.6.4 读写锁
- 11.6.6 条件变量
- 11.6.8 屏障
- 第 12 章:线程控制
- 《操作系统导论》OSTEP
- 第二部分:并发
- 《Unix 环境网络编程卷 2:进程间通信》
- 第 7 章:互斥锁和条件变量
- 第 10 章:POSIX 信号量
# Footnotes
[^1]: 《Unix 环境高级编程》P309
[^2]: 《Unix 环境高级编程》P320
[^3]: 《Unix 环境高级编程》P328
[^4]: 《Unix 环境高级编程》P332
[^5]: 《Unix 环境网络编程卷 2:进程间通信》P175
[^6]: 《Unix 环境高级编程》P465~469
[^7]: 《Unix 环境网络编程卷 2:进程间通信》P179
[^8]: 《Unix 环境高级编程》P336