Chromium 中的 Callback
2025-02-19
上次🔗 上次论了 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()
简直如出一辙。
有趣的地方在于状态管理。
对于引用绑定,有四种手段:
- 用 Raw Pointer,手动管理对象生命期
- 用 Weak Pointer,只在原始对象有效时调用,否则就是个空操作
- 用 Unique Pointer,排他性对象绑定,配合
std::move()
,析构时自动释放内存 - 用 Shared Pointer,底层用引用计数,确保调用时对象有效
回到最开始说的,Callback 使用时的难点,不在于逻辑实现,而在于状态管理。
4. 总结
任务是一个自洽的可执行单元。
复杂的问题,可以通过拆解,组合多个子任务完成。
这是归并排序的背后思想,是 Unix 哲学,同样契合 Map-Reduce 的理念。
将大问题,转化为多个小问题,小心翼翼地管理好小问题里的状态,大问题最终得以解决。
(完)
参考
- 本文作者:Plantree
- 本文链接:https://plantree.me/blog/2025/callback-in-chromium/
- 版权声明:所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
最后更新于: 2025-02-19T05:36:45+08:00