Async++ 源码分析17--fifo_queue.h

一、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 = 0tail = 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 重合时,队列已满(预留一个空位区分 “满” 和 “空”)。

  • 扩容逻辑

    1. 创建新数组 new_items,大小为原数组的 2 倍(保持幂等性,确保后续位运算有效)。
    2. 将原数组中的任务按顺序拷贝到新数组(原数组是循环的,需通过 (i + head) & (items.size() - 1) 读取真实顺序)。
    3. 重置 head = 0(新数组的头部从 0 开始)、tail = items.size() - 1(新数组的尾部为原数组大小减 1,即新数组已用满原数组大小的空间)。
    4. 通过 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(表示无任务可弹)。
  • 弹出任务
    1. 读取 head 指向的任务指针 x = items[head]
    2. head 向后移动一位(循环移动,位运算优化)。
    3. 将 void* 指针 x 转换回 task_run_handle(通过 from_void_ptr)并返回,外部可执行该任务。

2.4. 关键设计亮点与限制

设计亮点
  1. 循环队列 + 位运算优化:利用数组大小为 2 的幂的特性,通过 & (size-1) 替代取模运算,提升循环队列的操作效率。
  2. 缓存行对齐aligned_array 保证数组内存按缓存行对齐,减少多线程场景下的缓存伪共享。
  3. 动态扩容(翻倍):扩容时数组大小翻倍,既保证位运算有效性,又减少扩容频率(分摊扩容开销)。
  4. 资源安全回收:析构函数主动释放未执行的任务,避免内存泄漏。
限制
  • 非线程安全:代码注释明确指出 “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)提交任务即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值