一、Async++ 代码结构目录
Async++ 项目的目录结构清晰,主要包含根目录下的配置文件、源代码目录、头文件目录以及示例代码目录,具体结构如下:
asyncplusplus/
├── .gitignore # Git 忽略文件配置
├── Async++Config.cmake.in # CMake 配置模板文件
├── CMakeLists.txt # CMake 构建脚本
├── LICENSE # 许可证文件(MIT 许可证)
├── README.md # 项目说明文档
├── examples/ # 示例代码目录
│ └── gtk_scheduler.cpp # GTK 调度器示例
├── src/ # 源代码目录
│ ├── fifo_queue.h # FIFO 队列实现
│ ├── internal.h # 内部头文件(包含类型定义、宏等)
│ ├── scheduler.cpp # 调度器实现
│ ├── singleton.h # 单例模式实现
│ ├── task_wait_event.h # 任务等待事件实现
│ ├── threadpool_scheduler.cpp # 线程池调度器实现
│ └── work_steal_queue.h # 工作窃取队列实现
└── include/ # 头文件目录
├── async++.h # 主头文件(对外提供统一接口)
└── async++/ # 子模块头文件目录
├── aligned_alloc.h
├── cancel.h
├── continuation_vector.h
├── parallel_for.h
├── parallel_invoke.h
├── parallel_reduce.h
├── partitioner.h # 分区器相关定义
├── range.h # 范围(迭代器对)相关定义
├── ref_count.h
├── scheduler.h # 调度器接口定义
├── scheduler_fwd.h
├── task.h # 任务类定义
├── task_base.h # 任务基类定义
├── traits.h
└── when_all_any.h
二、traits.h源码分析
2.1 源码
// Copyright (c) 2015 Amanieu d'Antras
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef ASYNCXX_H_
# error "Do not include this header directly, include <async++.h> instead."
#endif
namespace async {
namespace detail {
// Queue which holds tasks in FIFO order. Note that this queue is not
// thread-safe and must be protected by a lock.
class fifo_queue {
detail::aligned_array<void*, LIBASYNC_CACHELINE_SIZE> items;
std::size_t head, tail;
public:
fifo_queue()
: items(32), head(0), tail(0) {}
~fifo_queue()
{
// Free any unexecuted tasks
for (std::size_t i = head; i != tail; i = (i + 1) & (items.size() - 1))
task_run_handle::from_void_ptr(items[i]);
}
// Push a task to the end of the queue
void push(task_run_handle t)
{
// Resize queue if it is full
if (head == ((tail + 1) & (items.size() - 1))) {
detail::aligned_array<void*, LIBASYNC_CACHELINE_SIZE> new_items(items.size() * 2);
for (std::size_t i = 0; i != items.size(); i++)
new_items[i] = items[(i + head) & (items.size() - 1)];
head = 0;
tail = items.size() - 1;
items = std::move(new_items);
}
// Push the item
items[tail] = t.to_void_ptr();
tail = (tail + 1) & (items.size() - 1);
}
// Pop a task from the front of the queue
task_run_handle pop()
{
// See if an item is available
if (head == tail)
return task_run_handle();
else {
void* x = items[head];
head = (head + 1) & (items.size() - 1);
return task_run_handle::from_void_ptr(x);
}
}
};
} // namespace detail
} // namespace async
这段代码是 async++ 库(一个用于 C++ 的异步任务调度库)的内部实现细节,定义了一个非线程安全的 FIFO(先进先出)任务队列 fifo_queue,用于存储待执行的异步任务。以下是对代码的逐部分解析,包括设计目的、核心逻辑和关键细节。
2.2. 头文件保护与命名空间
#ifndef ASYNCXX_H_
# error "Do not include this header directly, include <async++.h> instead."
#endif
namespace async {
namespace detail {
// ... 类定义 ...
} // namespace detail
} // namespace async
- 头文件保护:通过
#ifndef ASYNCXX_H_检查,禁止直接包含该文件(需通过顶层头文件<async++.h>引入)。这是因为该文件是库的内部实现头文件,不对外暴露接口,避免用户依赖不稳定的内部细节。 - 命名空间:
async:库的顶层命名空间,所有公开接口都在此命名空间下。async::detail:用于存放内部实现细节的子命名空间,表明该队列仅在库内部使用,不对外提供 API。
2.3. fifo_queue 类:核心 FIFO 任务队列
该类的核心功能是以 FIFO 顺序存储任务,但不保证线程安全(需外部加锁保护),设计上针对性能做了优化(如缓存行对齐、幂等大小扩容)。
2.3.1 成员变量
class fifo_queue {
detail::aligned_array<void*, LIBASYNC_CACHELINE_SIZE> items;
std::size_t head, tail;
// ... 构造/析构/成员函数 ...
};
-
items:任务存储数组- 类型
detail::aligned_array<void*, LIBASYNC_CACHELINE_SIZE>:aligned_array是 async++ 库的自定义数组类型,保证数组内存按LIBASYNC_CACHELINE_SIZE(缓存行大小,通常为 64 字节)对齐。目的:避免缓存伪共享(多个线程同时访问同一缓存行的不同数据,导致缓存频繁失效),提升多线程环境下的性能(尽管队列本身非线程安全,但数组对齐仍对外部锁保护的场景有帮助)。 - 存储内容:
void*指针(指向任务对象task_run_handle的底层数据,通过类型转换实现通用存储)。
- 类型
-
head与tail:队列指针head:指向队列的头部(下一个要弹出的任务位置)。tail:指向队列的尾部(下一个要插入的任务位置)。- 类型:
std::size_t(无符号整数,避免负数索引问题)。
2.3.2 构造函数与析构函数
fifo_queue()
: items(32), head(0), tail(0) {}
~fifo_queue()
{
// Free any unexecuted tasks
for (std::size_t i = head; i != tail; i = (i + 1) & (items.size() - 1))
task_run_handle::from_void_ptr(items[i]);
}
-
构造函数:
- 初始化
items数组大小为 32(幂等值,便于后续通过位运算实现 “循环队列”)。 - 初始化
head = 0、tail = 0(队列初始为空)。
- 初始化
-
析构函数:
- 核心逻辑:遍历队列中未执行的任务,通过
task_run_handle::from_void_ptr(items[i])释放任务资源(from_void_ptr会将void*转换回task_run_handle并触发其析构,避免内存泄漏)。 - 遍历方式:
i = (i + 1) & (items.size() - 1),利用位运算实现 “循环取模”(仅当items.size()是 2 的幂时成立,这也是数组初始大小为 32 且扩容时翻倍的原因),比(i+1) % items.size()更高效。
- 核心逻辑:遍历队列中未执行的任务,通过
2.3.3 核心成员函数:push(插入任务)
void push(task_run_handle t)
{
// Resize queue if it is full
if (head == ((tail + 1) & (items.size() - 1))) {
detail::aligned_array<void*, LIBASYNC_CACHELINE_SIZE> new_items(items.size() * 2);
for (std::size_t i = 0; i != items.size(); i++)
new_items[i] = items[(i + head) & (items.size() - 1)];
head = 0;
tail = items.size() - 1;
items = std::move(new_items);
}
// Push the item
items[tail] = t.to_void_ptr();
tail = (tail + 1) & (items.size() - 1);
}
push 函数的作用是将任务 t 插入队列尾部,核心逻辑分为扩容检查和插入任务两步:
步骤 1:检查队列是否已满,如需则扩容
-
满队列判断:
head == ((tail + 1) & (items.size() - 1))由于队列是 “循环队列”(head和tail会绕回数组开头),当tail加 1 后与head重合时,队列已满(预留一个空位区分 “满” 和 “空”)。 -
扩容逻辑:
- 创建新数组
new_items,大小为原数组的 2 倍(保持幂等性,确保后续位运算有效)。 - 将原数组中的任务按顺序拷贝到新数组(原数组是循环的,需通过
(i + head) & (items.size() - 1)读取真实顺序)。 - 重置
head = 0(新数组的头部从 0 开始)、tail = items.size() - 1(新数组的尾部为原数组大小减 1,即新数组已用满原数组大小的空间)。 - 通过
std::move转移新数组所有权到items(避免拷贝,提升性能)。
- 创建新数组
步骤 2:插入任务到队列尾部
items[tail] = t.to_void_ptr():将task_run_handle对象t转换为void*指针(通过to_void_ptr成员函数),存入tail指向的位置。tail = (tail + 1) & (items.size() - 1):tail向后移动一位(循环移动,通过位运算实现)。
2.3.4 核心成员函数:pop(弹出任务)
task_run_handle pop()
{
// See if an item is available
if (head == tail)
return task_run_handle(); // 返回空任务(表示队列空)
else {
void* x = items[head];
head = (head + 1) & (items.size() - 1);
return task_run_handle::from_void_ptr(x);
}
}
pop 函数的作用是从队列头部弹出一个任务,核心逻辑分为空队列检查和弹出任务两步:
- 空队列检查:
head == tail时队列空,返回默认构造的task_run_handle(表示无任务可弹)。 - 弹出任务:
- 读取
head指向的任务指针x = items[head]。 head向后移动一位(循环移动,位运算优化)。- 将
void*指针x转换回task_run_handle(通过from_void_ptr)并返回,外部可执行该任务。
- 读取
2.4. 关键设计亮点与限制
设计亮点
- 循环队列 + 位运算优化:利用数组大小为 2 的幂的特性,通过
& (size-1)替代取模运算,提升循环队列的操作效率。 - 缓存行对齐:
aligned_array保证数组内存按缓存行对齐,减少多线程场景下的缓存伪共享。 - 动态扩容(翻倍):扩容时数组大小翻倍,既保证位运算有效性,又减少扩容频率(分摊扩容开销)。
- 资源安全回收:析构函数主动释放未执行的任务,避免内存泄漏。
限制
- 非线程安全:代码注释明确指出 “this queue is not thread-safe and must be protected by a lock”,多线程同时
push/pop时需外部加锁(如std::mutex)。 - 依赖
task_run_handle:队列仅存储task_run_handle类型的任务(async++ 库的任务封装类型),不支持通用类型。
三. 总结
fifo_queue 是 async++ 库内部的基础任务存储组件,核心作用是为异步调度器提供一个高效的 FIFO 任务缓存。它通过循环队列、位运算优化、缓存行对齐等设计,在单线程(或外部加锁的多线程)场景下实现了高效的任务插入 / 弹出,同时保证资源安全回收。
该类的定位是 “库内部细节”,因此放在 detail 命名空间且禁止直接包含,体现了良好的代码封装性 —— 用户无需关心任务的存储实现,只需通过 async++ 库的公开接口(如 async::spawn)提交任务即可。

被折叠的 条评论
为什么被折叠?



