当前位置: 首页 > news >正文

Chromium 回调设计实战:BindOnce 与 BindRepeating 的最佳实践

在 Chromium 的 base 库中,base::BindRepeating 和 base::BindOnce 是两种用于创建回调的模板函数,它们的主要区别在于 回调的调用语义 和 所有权模型。以下是它们的核心区别和适用场景:


1. 核心区别

特性base::BindOncebase::BindRepeating
调用次数仅能调用 一次(移动语义)可调用 多次(复制语义)
所有权转移绑定参数和回调本身 只能移动std::move绑定参数可复制,回调可多次持有
性能更高效(避免引用计数开销)可能有额外开销(如引用计数)
典型用途异步任务、单次回调(如 PostTask事件监听、需要多次触发的回调(如信号槽)
是否支持 WeakPtr✅ 需显式调用 base::BindOnce + std::move✅ 直接支持

2. 详细对比

(1) 调用次数限制

  • base::BindOnce

    • 生成的 base::OnceCallback 只能调用一次,调用后失效(变为 nullptr)。

    • 适用于 一次性操作(如异步任务完成后的回调)。

    base::OnceCallback<void(int)> callback = base::BindOnce([](int x) { LOG(INFO) << x; });
    std::move(callback).Run(42);  // 调用一次,之后 callback 失效
    // std::move(callback).Run(43);  // 错误!callback 已无效
  • base::BindRepeating

    • 生成的 base::RepeatingCallback 可多次调用

    • 适用于 重复事件(如按钮点击、信号通知)。

    base::RepeatingCallback<void(int)> callback = base::BindRepeating([](int x) { LOG(INFO) << x; });
    callback.Run(42);  // 第一次调用
    callback.Run(43);  // 第二次调用(合法)

(2) 所有权和参数传递

  • base::BindOnce

    • 绑定参数和回调本身 通过移动语义传递std::move)。

    • 适合传递独占资源(如 std::unique_ptr)。

    auto task = std::make_unique<Task>();
    base::OnceCallback<void()> callback = base::BindOnce([](std::unique_ptr<Task> task) { task->Execute(); },std::move(task)  // 移交所有权
    );
  • base::BindRepeating

    • 绑定参数 需支持复制(或使用 base::RetainedRef 等包装器)。

    • 无法直接绑定 std::unique_ptr(除非手动管理生命周期)。

    int value = 42;
    base::RepeatingCallback<void()> callback = base::BindRepeating([](int x) { LOG(INFO) << x; },value  // 值被复制
    );

(3) 与 WeakPtr 的结合

  • base::BindOnce + WeakPtr

    • 需要显式使用 std::move 传递 WeakPtr

      base::BindOnce(&MyClass::OnDone, std::move(weak_ptr));
    • 调用时自动检查 WeakPtr 有效性(若对象已销毁,回调不执行)。

  • base::BindRepeating + WeakPtr

    • 直接绑定 WeakPtr,每次调用都会检查有效性:

      base::BindRepeating(&MyClass::OnEvent, weak_ptr);

3. 如何选择?

场景推荐使用原因
异步任务回调(如 PostTaskbase::BindOnce任务通常只执行一次,避免不必要的开销
事件监听(如按钮点击)base::BindRepeating事件可能多次触发
需要传递 std::unique_ptrbase::BindOnce移动语义更安全
需要支持跨线程回调两者均可但需配合 WeakPtr 或 base::RetainedRef 管理生命周期

4. 代码示例

(1) base::BindOnce 典型用法

// 异步任务完成后回调(单次)
void OnTaskComplete(base::OnceCallback<void(int)> callback) {std::move(callback).Run(42);  // 调用后 callback 失效
}// 绑定到 OnceCallback
auto callback = base::BindOnce([](int x) { LOG(INFO) << "Result: " << x; });
OnTaskComplete(std::move(callback));

(2) base::BindRepeating 典型用法

// 事件监听(多次触发)
class Button {public:void SetClickCallback(base::RepeatingCallback<void()> callback) {click_callback_ = std::move(callback);}void Click() { click_callback_.Run(); }private:base::RepeatingCallback<void()> click_callback_;
};// 绑定到 RepeatingCallback
Button button;
button.SetClickCallback(base::BindRepeating([]() { LOG(INFO) << "Clicked!"; }));
button.Click();  // 多次触发

好的,我将从 源码层面 深入分析 base::BindOnce 和 base::BindRepeating 的实现机制,结合 Chromium 的 base 库代码(基于最新稳定版本),解析它们的核心设计差异和性能关键点。


5. 核心类与模板结构

(1) 回调的基类:base::Callback

Chromium 的回调系统通过模板类 base::OnceCallback 和 base::RepeatingCallback 实现,二者均继承自 base::Callback 的模板特化。
源码路径:base/functional/callback.h

template <typename Signature>
class OnceCallback;  // 只能调用一次template <typename Signature>
class RepeatingCallback;  // 可多次调用

(2) 绑定工厂:base::Bind 系列

base::BindOnce 和 base::BindRepeating 是工厂函数,生成对应的回调对象。
关键源码片段:

// base/bind.h
template <typename Functor, typename... Args>
inline OnceCallback<MakeUnboundRunType<Functor, Args...>>
BindOnce(Functor&& functor, Args&&... args) {return BindImpl<OnceCallback>(std::forward<Functor>(functor),std::forward<Args>(args)...);
}template <typename Functor, typename... Args>
inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>>
BindRepeating(Functor&& functor, Args&&... args) {return BindImpl<RepeatingCallback>(std::forward<Functor>(functor),std::forward<Args>(args)...);
}

6. 底层实现机制

(1) 回调存储:BindState

所有绑定的参数和函数对象通过 BindState 存储,这是一个引用计数的内部类。
源码路径:base/bind_internal.h

template <typename Functor, typename... BoundArgs>
struct BindState {Functor functor_;std::tuple<BoundArgs...> bound_args_;// 引用计数控制(RepeatingCallback 使用)mutable scoped_refptr<RefCountedBase> ref_count_;
};
  • OnceCallback:直接持有 BindState 的独占所有权(类似 std::unique_ptr)。

  • RepeatingCallback:通过 scoped_refptr 共享 BindState(类似 std::shared_ptr)。

(2) 调用逻辑

回调的调用通过模板特化的 Run() 方法实现:

// OnceCallback 的调用(移动语义)
template <typename R, typename... Args>
R OnceCallback<R(Args...)>::Run(Args... args) && {// 调用后销毁内部状态auto state = std::move(bind_state_);return state->functor_.Run(std::forward<Args>(args)...);
}// RepeatingCallback 的调用(复制语义)
template <typename R, typename... Args>
R RepeatingCallback<R(Args...)>::Run(Args... args) const {// 无状态转移,可多次调用return bind_state_->functor_.Run(std::forward<Args>(args)...);
}

7. 关键性能差异

(1) 内存管理

回调类型存储方式开销
OnceCallback独占 BindState(移动)无原子操作,无引用计数
RepeatingCallback共享 BindState(引用计数)需要原子操作维护 ref_count_

(2) 参数传递优化

  • OnceCallback:支持移动语义绑定 std::unique_ptr 等独占类型。

    auto ptr = std::make_unique<int>(42);
    auto callback = base::BindOnce([](std::unique_ptr<int> p) {}, std::move(ptr));
  • RepeatingCallback:要求参数可复制(或使用 base::RetainedRef 包装)。


8. 线程安全性分析

(1) 回调本身的线程安全

  • OnceCallback:移动后原回调失效,跨线程传递需显式 std::move

  • RepeatingCallback:可安全复制到多个线程,但调用时需自行同步。

(2) 与 WeakPtr 的结合

  • OnceCallback:绑定 WeakPtr 时自动生成无效回调(调用时检查):

    // 内部实现:调用前检查 WeakPtr 是否有效
    template <typename T>
    void InvokeWithWeakPtr(T* obj) {if (!obj) return;  // WeakPtr 已失效functor_.Run(obj, std::forward<Args>(args)...);
    }
  • RepeatingCallback:每次调用都会检查 WeakPtr


9. 设计哲学总结

  1. OnceCallback

    • 零开销抽象:通过移动语义避免引用计数。

    • 强制单次调用:防止资源泄漏(如重复释放 std::unique_ptr)。

  2. RepeatingCallback

    • 灵活性优先:支持多次调用和跨线程共享。

    • 代价是性能:引用计数和参数复制可能引入开销。


10. 实际应用示例

(1) 单次任务回调(OnceCallback

base::ThreadPool::PostTask(FROM_HERE,base::BindOnce([](std::unique_ptr<Data> data) {ProcessData(std::move(data));},std::make_unique<Data>()));

(2) 事件监听(RepeatingCallback

class Button {public:void SetClickCallback(base::RepeatingClosure callback) {callback_ = std::move(callback);}void Click() { callback_.Run(); }private:base::RepeatingClosure callback_;
};

11. 从源码学到的优化技巧

  1. 优先用 OnceCallback:除非需要多次调用,否则避免引用计数开销。

  2. 移动语义绑定:对独占资源(如 std::unique_ptr)使用 BindOnce

  3. 避免跨线程持有 RepeatingCallback:改用 PostTask + OnceCallback 减少竞争。


通过源码分析可见,Chromium 通过 模板元编程 和 移动语义 极致优化了回调性能,而 OnceCallback/RepeatingCallback 的区分正是对 资源所有权 和 调用语义 的精确控制。

相关文章:

  • stm32如何触摸屏设置显示按钮
  • JetpackCompose基础学习2.2
  • C++ 与 Python 内存分配策略对比
  • C# 枚举 详解
  • LeetCode 217.存在重复元素
  • C++:因子问题
  • 【TTS回顾】Bert-VITS2深度解析:融合BERT的多语言语音合成模型
  • Python爬虫实战:获取国家统计网最新消费数据并分析,为从业者做参考
  • Spring Boot入门案例(Spring Initializr方式,IDEA版)
  • FANUC发那科焊接机器人智能气阀
  • Windows环境使用NVM高效管理多个Node.js版本
  • 可重入(Reentrant) vs 线程安全(Thread-Safe)
  • AI Agent开发第71课-一个完善的可落地企业AI Agent全架构
  • 视觉-语言导航:综述与类别
  • idea2024 不知道安装了什么插件,界面都是中文的了,不习惯,怎么修改各个选项改回英文
  • 网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】
  • upload-labs靶场通关详解:第11关
  • Java后端面试八股文大全(2025最新版)
  • 【八股战神篇】Java多线程高频面试题(JUC)
  • MongoDB及spring集成
  • 人民日报评论员观察:稳企业,全力以赴纾困解难
  • 三人在共享单车上印小广告被拘,北京警方专项打击非法小广告
  • 新华每日电讯:博物馆正以可亲可近替代“高冷范儿”
  • 美国失去最后的AAA主权评级,继标普、惠誉后再遭穆迪降级
  • 爬坡难下坡险,居民出行难题如何解?
  • “朱雀玄武敕令”改名“周乔治华盛顿”?警方称未通过审核