
执行器的概念最早从AimRT接触,虽偶有使用,但一直未进行深入研究。本文结合Asio库及网络资料,对执行器(Executor)这一概念进行梳理,其在网上的系统性资料相对较少。
执行器是一个在并发编程中由来已久的概念,它表示一个能够执行逻辑代码的抽象实体。一个执行器可以是一个线程池、一个协程或纤程,可以是CPU或GPU,甚至可以是远端的一台服务器。我们日常编写的最简单代码也运行在一个默认的执行器上:主线程。
一个基础执行器接口可能如下:
void Execute(std::function<void()>);
该接口表示,可以将一个任务闭包投递到指定的执行器中去执行。至于这个任务在何时何地执行,则完全依赖于具体执行器的实现。C++标准库中的 std::thread 就是一个典型的执行器,其构造函数接受一个任务闭包,并将该任务放在一个新线程中执行。
为了更好地理解执行器模型,需要厘清几个关键术语:
- executor: 一个轻量的句柄,指向一个执行上下文。它定义了如何提交任务,例如决定任务是稍后执行还是立即执行。
- context: 实际协调函数执行的结构,包含一个可供所有关联executor使用的共享数据存储区域,例如一个计时器服务。
C++执行器是一种抽象,指能够执行函数或异步任务的实体。其核心价值在于将“做什么”(任务)与“在哪里执行”(线程/上下文)分离开来,提供了对异步操作灵活调度和控制的能力。该模型的核心是Sender/Receiver模型(或称生产者/消费者模型),它允许开发者定义任务(Sender)、结果接收器(Receiver),并通过 connect、start 等操作,实现任务在特定上下文(如线程池)中的非阻塞、解耦执行。这正是现代C++并发编程,例如Asio网络库以及未来C++标准库所依赖的重要基石。
执行器决定了任务在哪里运行,它可以是一个线程、一个线程池、一个专用线程,甚至是一种调度策略。
其主要目的和优势包括:
- 解耦: 将任务的定义与执行上下文解耦,极大提高了代码的可复用性和模块化程度。
- 泛型化: 提供了比传统的
future/promise 模型更通用、更强大的抽象,支持构建更复杂的异步算法。
- 高效与优化: 惰性求值和强大的组合能力,给予了编译器和运行时系统更多的优化空间。
- 上下文控制: 允许开发者精确控制异步操作在哪个具体的线程或线程池上运行,这对于性能调优和并发编程中的数据安全至关重要。
简而言之,C++执行器通过标准化一套“生产者-消费者”模型来处理异步操作,使开发者能够以声明式的方式,清晰地定义“谁”在“哪里”执行“什么”工作。
下面是一个使用Asio库的简单示例:
#include <asio.hpp>
#include <iostream>
int main() {
asio::io_context ctx;
auto executor = ctx.get_executor(); // 获取执行器
// 提交一个异步任务(保证会投递到执行器队列)
asio::post(executor, []() {
std::cout << "Task 1 executed by executor" << std::endl;
});
// 尝试立即执行(如果当前正在 ctx.run 的线程中)
asio::dispatch(executor, []() {
std::cout << "Task 2 executed by executor" << std::endl;
});
ctx.run(); // 运行事件循环,处理所有已提交的任务
return 0;
}
理解执行器模型后,其最终价值仍需落到具体的业务抽象上。在设计系统时,关键在于根据实际业务场景,判断是否以及如何利用执行器来解耦任务调度与执行,这需要结合如std::thread等基础并发知识进行综合考量与设计。
|