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

ESP32-P4 USB Host鼠标开发实战:从物理层握手到HID解析 USB Host是嵌入式系统接入标准外设的关键能力,其核心在于设备枚举、描述符解析与中断传输三大机制。ESP32-P4虽支持USB Host模式,但受限于PHY硬件设计、ESP-IDF驱动对低速设备时序容忍度不足、HID Report Descriptor动态解析缺失等问题,导致真实鼠标识别率低、坐标跳变、丢包严重。本文围绕USB 2.0低速设备通信原理,深入剖析D+/D-电平配置错误、设备/配置描述符请求超时、Report ID与数据布局不匹配等典型故障,并结合ESP-IDF v5.3.1固件环境,提供硬 阅读详情

在 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. 如何选择?

场景推荐使用原因
异步任务回调(如 PostTask)base::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 的区分正是对 资源所有权 和 调用语义 的精确控制。

Chromium Mojo C++ Bindings API(中文) chromium mojo机制最新版 阅读详情

相关推荐

在指定的CEF线程上执行任务

CEF有几种线程,比如TID_UI、比如TID_RENDER,我们可以指定某个任务在某个线程上执行。

安晓辉生涯——聚焦程序员的职业规划与成长 1万+

多线程开发中,多用消息传递,少用锁

有几个注入点可以从其实现的外部控制绑定行为。};}};如果在方法base::Bind的接收器上为true ,则 检查接收器的评估结果是否为true,并在其评估为false时取消调用。您可以专门将外部智能指针作为弱指针。在调用目标函数之前,为每个绑定的参数调用。您可以专注这个定义参数的包装,例如,,和。Windows提供了非常原始的工具来构建用户界面。该系统提供了一些基本控件和本机窗口容器,但是构建自定义用户界面很困难。

m0_73257876的博客 706

指纹浏览器内存泄漏的梦魇:成百上千实例并发的内存优化与生命周期管理

本文将直插 Chromium 的内存管理心脏,从 C++ 底层原理到分布式架构设计,深度拆解成百上千实例并发下的内存优化与生命周期管理,彻底终结这梦魇般的循环。

数据知道的博客 334

chromium源码中的callback

chromium

qq_45329237的博客 1724

CEF线程模型与初始化过程详解

上一篇说了CEF框架的消息传递,实际上这个消息传递指的是进程之间的消息传递,也就是IPC的方式在Browser,Render进程之间传递消息。这一篇就接上篇,说说CEF的线程模型,因为在CEF框架的main entry中,主要的三个函数CefInitialize、CefRunMessageLoop和CefShutdown都和这个模型有关。

新兴IT民工的专栏 2128

ThingsBoard实战指南:RPC命令下发与设备交互详解

本文深入解析了ThingsBoard平台中RPC命令下发的核心机制与实战应用。通过对比客户端与服务端RPC模式,详细阐述了从仪表盘控件配置到设备端Python代码响应的完整双向交互流程,并提供了常见问题排查与进阶优化技巧,帮助开发者构建可靠、实时的物联网设备控制系统。

weixin_29168153的博客 211

Chrome 中的 Threading 和 Tasks

线程 每个 chrome 进程都包含有: 一个主线程: 在 browser 进程中负责:更新 UI; 在 renderer 进程中负责:运行 Blink; 一个 IO 线程: 在 browser 进程中负责:处理 IPC 和网络请求; 在 renderer 进程中负责:处理 IPC 请求; 一些处理特殊操作的线程; 一个处理通用操作的线程池; 大部分的线程都包含有一个...

相见不如怀念 2719

告别130MB/s瓶颈!用Zynq+NVMe实现1.2GB/s高速存储的PL数据分流实战

本文详细介绍了如何通过Zynq+NVMe实现1.2GB/s高速存储的PL数据分流方案,突破传统Zynq架构130MB/s的瓶颈。文章分享了PL直连NVMe的硬件架构设计、软件栈优化及性能调优实战技巧,帮助开发者实现高速数据流的稳定存储。

weixin_42653691的博客 371

《Threading and Tasks in Chrome》笔记

学习资料:链接

Haipai1998的博客 1291

qt实现程序密钥注册功能,MD5加密+AES加密,并实现一台电脑系统一个密钥

qt实现了一个给自己软件加密的功能。 原理是: 需要被注册的软件第一次运行的时候,在系统的注册表里面注册一个键值,如下: 这个值作为生成密钥的用户识别号,然后生成密钥文件key.db,把这个key.db文件放到软件执行目录下,即可完成软件的注册并正常使用软件。 软件第一次打开的界面及密钥生成工具软件界面如下: 将密钥生成工具软件生成的key.db放到软件执行目录下之后,软件即可正常使用,如下图: 代码使用了MD5加密 外加 AES加密, 解密和加密的核心代码如下: QSt.

weixin_43935474的博客 4127

PnP 单目相机位姿估计(二):solvePnP利用二维码求解相机世界坐标

前言 原理简介 输入参数准备 1 objectPoints特征点世界坐标 2 imagePoints特征点在摄像头下的像素点坐标 3cameraMatrixdistCoeffs内参矩阵和畸变矩阵 相机世界坐标的求解 1求世界坐标中的点在相机坐标系下的坐标 2求相机在世界坐标中的坐标 3效果求解相机在世界坐标系下的坐标点 4求解代码1.前言  很多时候,当我们利用单目相机获取图像时,往往只能获取图像中

cocoaqin的博客 5万+

chrome-base 如何实现一个BindOnce

BindOnce 是如何实现的呢?翻看源码:base\functional\bind.h写的 非常简洁重要的也就是最后一句:构造一个BindImpl,同时完美转发BindState的实现其中用到了std::tuple??

LIJIWEI0611的博客 1295

【WRF-DART第一期】WRF/DART概述

【WRF-DART第一期】WRF/DART概述

WW、forever的博客 107

chromium中mojom中增加接口

我们因为使用mojo video decoder,所以增加一个获取底层平台的接口,从DecoderStream中获取,就需要在mojom::VideoDecoder中增加接口。是一个跨平台IPC框架,它诞生于chromium,用来实现chromium进程内/进程间的通信。中得到frames_rendered值之后,然后通过callback调用,把这个值给传出去。的定义,可以看到这个方法是纯虚函数,需要自己实现具体的功能,函数参数变成这个。

技术笔记 1544

dToF直方图之美_deadtime死区时间

IMX459的死区时间可以在6ns,这个时间是什么级别,车载激光雷达激光器的光脉宽一般都在4ns以上,也就是这个时间贴近去SPAD的死区时间,这就导致了pile up会非常弱,不会有太多数量的光子计数堆积,加上某些SPAD设计,对TDC有一些更深入的考量,所以不需要去校正pile up算法。Sensor层面决定了pile up的复杂程度,如果一个dToF的SPAD一致性非常好,那么pile up校正可以简单到极致,也就是一句代码的事,比如IMX611,优秀到不需要后端去考虑校正pile up。

致力于lidar应用开发 2147

【图像超分】论文复现:轻量化超分 | 残差特征蒸馏网络RFDN的Pytorch源码复现,跑通源码,整合到EDSR-PyTorch中进行训练,补充CCA的实现!

本文介绍了将RFDN(残差特征蒸馏网络)整合到EDSR-PyTorch框架中的实现过程。主要内容包括:1)环境配置与数据集准备;2)测试源码提供的x4预训练模型,并通过修改图像裁剪函数解决HR与SR尺寸不一致问题;3)训练过程详解,包括将RFDN源码模块整合到EDSR框架中,调整模型参数输入方式。文章还提供了完整的代码实现、训练好的模型权重下载链接,并对RFDN架构进行了详细拆解与注释。通过将RFDN与EDSR相结合,实现了轻量级图像超分辨率重建,获得了PSNR/SSIM等指标及可视化结果。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 432

chromium线程模型(1)--- 官方文档

这段代码主要介绍了Chrome浏览器的多进程和多线程架构。Chrome具有多进程架构,每个进程都是高度多线程的。本文将介绍每个进程共享的基本线程系统。主要目标是保持主线程(在浏览器进程中称为“UI”线程)和IO线程(每个进程用于接收IPC的线程)响应灵敏。这意味着将任何阻塞的I/O或其他昂贵的操作卸载到其他线程。我们的方法是使用消息传递作为线程之间通信的方式。我们不鼓励使用锁定和线程安全的对象。相反,对象只存在于一个(通常是虚拟的——稍后我们将讨论!)线程上,我们通过这些线程之间传递消息进行通信。

woai110120130的专栏 1602

应用商店评论分析实战:Python逆向工程破解Google Play加密接口与NLP建模

在移动互联网时代,应用商店中的用户评论成为了开发者优化应用、提升用户体验的重要依据。然而,Google Play等应用商店对评论数据进行了加密处理,直接爬取难度较大。本文将介绍如何使用Python逆向工程破解Google Play的加密接口,实现对应用商店评论的实时采集,并结合NLP(自然语言处理)技术进行情感分析和主题建模,为开发者提供有价值的数据支持。

u014481728的博客 1346

chrome怎么调用硬件_利用ASLR薄弱点:Chrome沙箱逃逸漏洞分析

概述我们在Chrome中,发现了一系列可能存在的沙箱逃逸漏洞。在此之后,我们认为可以将这些漏洞中的一个与渲染器存在的漏洞共同利用,形成一条完整的漏洞利用链,同时也能够让我们更好地理解现代Chrome漏洞利用所需的机制。考虑到全部可用的漏洞,最有可能被利用的是Issue 1755,这一Use-After-Free漏洞与经典的JavaScript引擎回调漏洞相似。目前看来,这是一个很好的候...

weixin_39611725的博客 522

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Chrome base 库详解:工具类和常用类库

本文深入解析了Chrome的base库,详细介绍了其中的工具类和常用类库。这些类库为开发者提供了一系列强大的工具和功能,帮助他们更有效地进行Chrome开发。

陆业聪 3509

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Chromium回调机制的隐秘角落:当const &参数遇见base::BindOnce

Chromium回调机制中base::BindOnce对const引用的处理体现了精妙的设计权衡。通过std::decay_t进行类型转换,将const引用参数转为值类型存储,默认执行拷贝而非传递引用,这是基于浏览器内核开发的特殊考量:优先保证安全性,避免悬垂引用风险。该设计充分利用C++类型系统特性,在存储阶段进行类型擦除,调用时自动重建引用,实现了零成本抽象。开发者可通过std::cref显式指定引用语义,但需自行确保对象生命周期。这种在安全与性能间的平衡,展现了Chromium工程团队对C++特性的深

John_ToStr的博客 680

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Vue-Bind-Once 使用指南

Vue-Bind-Once 使用指南 项目目录结构及介绍 Vue-Bind-Once 是一个轻量级的 Vue.js 指令库,用于在 Vue 应用中实现一次性数据绑定,特别适合服务器端渲染(SSR)场景。以下是该开源项目的基本目录结构及其简介: vue-bind-once/ │ ├── src # 核心源代码目录 │ ├── index.js #...

gitblog_00461的博客 460

selenium获取请求返回内容的解决方案

selenium获取请求返回内容的解决方案有更新! zz:http://xiajl.cn/articles/2018/12/28/1546004181844.html 提出问题 之前我的一篇博客说的是怎么利用selenium来做自动化监控。当出现异常时,我们需要记录页面源码、网络请求数据、截图等信息来方便我们诊断问题,基本上就够用了。但是,这两天遇到一个棘手的异常,时不时页面会弹出:“系统繁忙,请稍候再试!”,这时候我们去看网络请求数据,结果状态码全部都是200,没有其它信息,这压根没法定位...

bigcarp的专栏 7385

cef 框架 获取窗口句柄返回null

【代码】cef 框架 获取窗口句柄返回null。

cucibala的博客 452

Android 基类Base的封装

Android内部线程集中管理,不能直接new Handler new WebView? -- Base基类等封装- https://github.com/jenly1314/Base/tree/master/src/main/java/com/king/base Android 自定义Activity基类- http://blog.youkuaiyun.com/u013045971/article/d...

ShareUs的专栏 1487

Chrome中的线程与任务

原文链接:https://chromium.googlesource.com/chromium/src/+/master/docs/threading_and_tasks.md Chrome具有多进程体系结构,并且每个进程都是高度多线程的。 在本文档中,我们将介绍每个进程共享的基本线程系统。 主要目标是使主线程(在浏览器进程中也称为“ UI”线程)和IO线程(用于处理IPC的每个进程的线程)保持响应。 这意味着将任何阻塞的I / O或其他昂贵的操作卸载到其他线程。 我们的方...

LIJIWEI0611的博客 2759
上一篇: 用 RefCounted + WeakPtr 构建线程安全的异步模块
下一篇: Chromium 浏览器核心生命周期剖析:从 BrowserProcess 全局管理到 Browser 窗口实例
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ปรัชญา แค้วคำมูล

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值