Chromium 中的 Callback

2025-02-19 pv

上次🔗 上次论了 Chromium 的多任务调度。

这次打算聊聊更加基础的一个封装:任务(Task)。

为了便于理解,本打算仿照 Chromium 的 base 库自己实现一套极简的工具类,写的时候发现难度比较大,就放弃了。

这次就纸上谈兵。

Chromium 中,任务用 Callback 表示。官方文档说,设计之初的灵感来源之一,是 C++ 里的 tr1::function / tr1::bind

文章分三块:什么是任务,怎么封装,Chromium 里的实现。

1. 什么是任务

计算机的工作,是完成开发者指定的任务。

如果是人,要做一件事,首先要知道需要哪些材料,其次得知道怎么做。

比方说厨师,得有菜,得有厨具,还得有做饭的手艺。

计算机同理。

说白了,一份任务通常由两部分构成:

  • 状态(State)
  • 操作(Operation)

更极致的,在 函数式编程🔗 函数式编程,只有操作,没有状态,比如 Lisp、Schema,以及 C++ STL 里的 Functor。

普通一点,把状态和操作放置在一个 闭包🔗里。

任务,就是一个闭包,包含所要做事情的全部上下文(Context)。可以作为一个原子,在不同线程,不同时间上进行调度。

2. 如何封装

封装任务,就是封装闭包。

这是编程语言层面该做的事情。

在 C++ 中,创建函数,就是生成一个闭包:允许给定输入,内部处理,生成输出。

甚至可以捕获外部的变量。

C++ 11 提供了 Lambda 表达式🔗

与普通函数不同,Lambda 表达式,不仅可以接收输入,还可以将外部状态捕捉到内部。

C++ 里的捕获有两种形式:

  • 值捕获,可能存在低效的拷贝
  • 引用捕获,必须保证被引用对象的生命期够长

这就涉及到编程里最复杂的部分:状态管理。

纯函数式编程往往对此嗤之以鼻。众所周知,状态越多,越容易出错。

C++ 用 智能指针🔗,简化状态管理。

3. Chromium 里的实现

Chromium 的实现,是 Callback<>Bind() 组合。

有些任务是“阅后即焚“,用 OnceCallback<> 表示,有些需要重复使用,用 RepeatingCallback<>

Run() 方法用于立即执行。

Bind() 方法用于固定某些调用时的参数。

目前为止,与 C++ 11 中的 std::function<>std::bind() 简直如出一辙。

有趣的地方在于状态管理

对于引用绑定,有四种手段:

  1. 用 Raw Pointer,手动管理对象生命期
  2. 用 Weak Pointer,只在原始对象有效时调用,否则就是个空操作
  3. 用 Unique Pointer,排他性对象绑定,配合 std::move(),析构时自动释放内存
  4. 用 Shared Pointer,底层用引用计数,确保调用时对象有效

回到最开始说的,Callback 使用时的难点,不在于逻辑实现,而在于状态管理

4. 总结

任务是一个自洽的可执行单元。

复杂的问题,可以通过拆解,组合多个子任务完成。

这是归并排序的背后思想,是 Unix 哲学,同样契合 Map-Reduce 的理念。

将大问题,转化为多个小问题,小心翼翼地管理好小问题里的状态,大问题最终得以解决。

(完)

参考

  1. Chromium Docs - OnceCallback<> and BindOnce(), RepeatingCallback<> and BindRepeating()🔗
在 GitHub 上编辑本页面

最后更新于: 2025-02-19T05:36:45+08:00