用 RefCounted + WeakPtr 构建线程安全的异步模块
在 Chromium 的多线程异步编程中,合理管理对象生命周期非常关键。本文深入介绍 base::RefCountedThreadSafe
和 base::WeakPtr
的组合使用方法,并通过示例分析其使用要点及易踩的坑。
🌱 基础概念回顾
1. RefCountedThreadSafe<T>
-
是 Chromium 中线程安全的引用计数基类,用于实现对象的自动释放。
-
常和
scoped_refptr<T>
配合使用,确保对象在所有引用释放后自动析构。 -
使用方式:
class MyObject : public base::RefCountedThreadSafe<MyObject> { public: void DoSomething(); private: friend class base::RefCountedThreadSafe<MyObject>; ~MyObject(); // 析构必须是 private 或 protected };
2. WrapRefCounted
-
用于在已有裸指针(如
this
)的场景下安全构造scoped_refptr
。scoped_refptr<MyObject> ptr = base::WrapRefCounted(this);
🧪 示例代码:结合 RefCountedThreadSafe
与异步调用
class MyTaskRunner : public base::RefCountedThreadSafe<MyTaskRunner> { public: void PostWork() { base::ThreadPool::PostTask( FROM_HERE, base::BindOnce(&MyTaskRunner::DoWork, base::WrapRefCounted(this))); } private: friend class base::RefCountedThreadSafe<MyTaskRunner>; ~MyTaskRunner() = default; void DoWork() { LOG(INFO) << "Work done!"; } };
✅ 这里通过
WrapRefCounted(this)
绑定异步任务,确保任务执行时对象仍然存活。
⚠️ 易错用法及反例
❌ 错误示例 1:异步任务绑定裸指针,导致 use-after-free
// 错误:this 可能在异步任务执行前就被释放 base::ThreadPool::PostTask( FROM_HERE, base::BindOnce(&MyTaskRunner::DoWork, this));
❌ 错误示例 2:RefCounted 派生类析构函数为 public
class Wrong : public base::RefCountedThreadSafe<Wrong> { public: ~Wrong() {} // ⚠️ 应为 private,否则会导致外部手动 delete,破坏计数机制 };
❌ 错误示例 3:跨线程误用 WeakPtr
base::WeakPtr<MyObject> weak_ptr = weak_factory_.GetWeakPtr(); // 另一线程中使用 weak_ptr -> 不安全!
🔄 RefCounted 与 WeakPtr 的配合使用
当你希望:
-
对象生命周期由引用计数管理(如模块长时间存在)
-
同时避免回调访问已经销毁的对象
你可以使用 RefCounted + WeakPtr 同时配合:
class MyService : public base::RefCountedThreadSafe<MyService> { public: MyService() : weak_factory_(this) {} void StartAsyncTask() { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::BindOnce([](base::WeakPtr<MyService> weak_self) { if (!weak_self || weak_self.WasInvalidated()) return; weak_self->DoSomething(); }, weak_factory_.GetWeakPtr()), base::Seconds(3)); } private: friend class base::RefCountedThreadSafe<MyService>; ~MyService() = default; void DoSomething() { LOG(INFO) << "Safe async work"; } base::WeakPtrFactory<MyService> weak_factory_; };
✅ 推荐使用模式
场景 | 推荐方案 |
---|---|
异步回调确保对象存活 | WrapRefCounted(this) |
回调中避免悬挂指针 | WeakPtr + WasInvalidated() 检查 |
复杂模块管理生命周期 | RefCountedThreadSafe + WeakPtrFactory |
🧷 调试技巧与源码入口
常见断点位置
# RefCounted 增减引用 break base::internal::RefCountedThreadSafeBase::AddRef break base::internal::RefCountedThreadSafeBase::Release
Chromium 源码路径
-
base/memory/ref_counted.h
-
base/memory/weak_ptr.h
-
异步任务绑定:
base/bind.h
,base/task/post_task.h
📌 小结
-
RefCountedThreadSafe
用于自动管理对象生命周期,避免显式 delete。 -
WeakPtr
可防止异步访问已销毁对象,但不能延长生命周期。 -
WrapRefCounted(this)
是在this
已构造完毕后引入scoped_refptr
的安全方式。 -
多线程/异步任务中合理组合这两者,是写好浏览器模块的重要实践技能。