单例模式(Singleton)也称为单件模式,其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出,GUI 应用必须是单鼠标,操作系统只会弹出一个任务管理器等。

在我们的项目中使用了 Chrome 提供的 base::AtExitManager 类

该类负责类的内存回收问题,AtExitManager 模仿的就是 atexit 函数的功能,使用的时候,可以在 WinMain 函数中定义一个 AtExitManager 实例:

base::AtExitManager exit_manager;

在需要创建单例的地方使用 base::Singleton,官方的 singleton.h 提供了一个例子

// Example usage:
//
// In your header:
namespace base {
template <typename T>
struct DefaultSingletonTraits;
}
class FooClass {
public:
static FooClass* GetInstance(); <-- See comment below on this.
void Bar() { ... }
private:
FooClass() { ... }
friend struct base::DefaultSingletonTraits<FooClass>; DISALLOW_COPY_AND_ASSIGN(FooClass);
}; // In your source file:
#include "base/memory/singleton.h"
FooClass* FooClass::GetInstance() {
return base::Singleton<FooClass>::get();
}

简单的说就是创建一个 GetInstance 方法,再用 base::Singleton 提供的方法创建一个指针,这个指针指向类的对象,创建对象的方法被隐藏在 base::Singleton 中,我们在调用类的方法时候,只需要

FooClass::GetInstance->bar();

好奇该类如何被创建的,可以看它的源码,大致是这样

template <typename Type,
typename Traits = DefaultSingletonTraits<Type>,
typename DifferentiatingType = Type>
class Singleton {
private:
// Classes using the Singleton<T> pattern should declare a GetInstance()
// method and call Singleton::get() from within that.
friend Type* Type::GetInstance(); // Allow TraceLog tests to test tracing after OnExit.
friend class internal::DeleteTraceLogForTesting; // This class is safe to be constructed and copy-constructed since it has no
// member. // Return a pointer to the one true instance of the class.
static Type* get() {
#if DCHECK_IS_ON()
// Avoid making TLS lookup on release builds.
/* if (!Traits::kAllowedToAccessOnNonjoinableThread)
ThreadRestrictions::AssertSingletonAllowed(); */
#endif // The load has acquire memory ordering as the thread which reads the
// instance_ pointer must acquire visibility over the singleton data.
subtle::AtomicWord value = subtle::Acquire_Load(&instance_);
if (value != 0 && value != internal::kBeingCreatedMarker) {
return reinterpret_cast<Type*>(value);
} // Object isn't created yet, maybe we will get to create it, let's try...
if (subtle::Acquire_CompareAndSwap(&instance_, 0,
internal::kBeingCreatedMarker) == 0) {
// instance_ was NULL and is now kBeingCreatedMarker. Only one thread
// will ever get here. Threads might be spinning on us, and they will
// stop right after we do this store.
Type* newval = Traits::New(); // Releases the visibility over instance_ to the readers.
subtle::Release_Store(&instance_,
reinterpret_cast<subtle::AtomicWord>(newval)); if (newval != NULL && Traits::kRegisterAtExit)
AtExitManager::RegisterCallback(OnExit, NULL); return newval;
} // We hit a race. Wait for the other thread to complete it.
value = internal::WaitForInstance(&instance_); return reinterpret_cast<Type*>(value);
} // Adapter function for use with AtExit(). This should be called single
// threaded, so don't use atomic operations.
// Calling OnExit while singleton is in use by other threads is a mistake.
static void OnExit(void* /*unused*/) {
// AtExit should only ever be register after the singleton instance was
// created. We should only ever get here with a valid instance_ pointer.
Traits::Delete(reinterpret_cast<Type*>(subtle::NoBarrier_Load(&instance_)));
instance_ = 0;
}
static subtle::AtomicWord instance_;
}; template <typename Type, typename Traits, typename DifferentiatingType>
subtle::AtomicWord Singleton<Type, Traits, DifferentiatingType>::instance_ = 0; } // namespace base

也就是上图标红的一行,如下,

Type* newval = Traits::New();

单例创建完后,又是如何析构的呢,base::AtExitManager 帮我们做好了,看下面源码,

AtExitManager::~AtExitManager() {
if (!g_top_manager) {
NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
return;
}
DCHECK_EQ(this, g_top_manager); if (!g_disable_managers)
ProcessCallbacksNow();
g_top_manager = next_manager_;
} // static
void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
DCHECK(func);
RegisterTask(base::Bind(func, param));
}

我们开头提到在 WinMain 函数中定义一个 AtExitManager 实例,程序结束后,栈上的内存自动释放,这时调用 AtExitManager 的析构函数释放使用 RegisterCallback 函数注册的回调函数

因为 base::Singleton 创建单例的时候自动注册了回调函数,见上面 base::Singleton 第二个标红处,如下,

AtExitManager::RegisterCallback(OnExit, NULL);

base::AtExitManager 内部会有一个链表维护这些实例,这样析构时也会逐个释放

一些有用的文章:

base::AtExitManager 和 base::Singleton 的结合使用的更多相关文章

  1. convert from base 10 to base 2

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION Hence, we convert fro ...

  2. Base系列编码浅析【base16 base32 base64 base85 base36 base 58 base91 base 92 base62】

    Base系列编码浅析 [base16   base32   base64   base85  base36  base 58  base91  base 92   base62]     base编码 ...

  3. 关于BASE 24 ,BASE 64原理以及实现程序

    关于BASE 24 ,BASE 64原理以及实现程序 来源 https://wangye.org/blog/archives/5/ 可能很多人听说过Base64编码,很少有人听说过Base24编码,B ...

  4. ADS1.2中RO base与RW base

    ARM映像文件 ARM中的各种源文件(包括汇编文件,C语言程序及C++程序等)经过ARM编译器编译后生成ELF(Executable and linking format)格式的目标文件.这些目标文件 ...

  5. int('x', base)中的base参数

    >>> int('12', 16) 16表示'12'就是16进制数,int()要将这个16进制数转化成10进制.

  6. Appkiz.Base、Appkiz.Base.Languages

    环境: ILSpy version 4.0.0.4319-beta2 选择 C#6.0 Visual Studio 2015 直接保存代码,直接用Visual Studio 2015打开.csprj文 ...

  7. Base 64 编码

    原创地址:http://www.cnblogs.com/jfzhu/p/4020097.html 转载请注明出处 (一)Encoding VS. Encryption 很多人都以为编码(Encodin ...

  8. HTML/Elements/base

    https://www.w3.org/wiki/HTML/Elements/base HTML/Elements/base < HTML‎ | Elements   List of Elemen ...

  9. The POM for XXX:jar:${com.ld.base.service.version} is missing, no dependency information available

    最近有个jar改了名字后,有个依赖它的工程死活引用的是老名字,导致打包的时候出错,如下所示: [INFO] ---------------------------------------------- ...

  10. 封装常用的js(Base.js)——【01】理解库,获取节点,连缀,

    封装常用的js(Base.js)——[01]理解库,获取节点,连缀,  youjobit07 2014-10-10 15:32:59 前言:       现如今有太多优秀的开源javascript库, ...

随机推荐

  1. Kafka的部分初始化参数的学习与整理

    Kafka的部分初始化参数的学习与整理 背景 前段时间跟同事一起处理过kafka的topic offset的retention 时间与 log 的retention时间不一致. 导致消息还有, 但是o ...

  2. [转帖] Linux文本命令技巧(上)

    Linux文本命令技巧(上)   原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介# 前一篇我介绍了awk,这是一个全能的文本处理神器,因为它本身就是一门编程语言了 ...

  3. 源码学习之Spring容器创建原理

    1 前言 众所周知,Spring可以帮我们管理我们需要的bean.在我们需要用到这些bean的时候,可以很方便的获取到它,然后进行一系列的操作.比如,我们定义一个bean MyTestBean pub ...

  4. 文盘Rust -- 领域交互模式如何实现

    作者:京东科技 贾世闻 文盘Rust -- 领域交互模式如何实现 书接上文,上回说到如何通过interactcli-rs四步实现一个命令行程序.但是shell交互模式在有些场景下用户体验并不是很好.比 ...

  5. 【计算几何,数学】7.14 T3 @ xdfz

    Problem Link 给定 \(n\) 个球和一个点 \(P\),求点 \(P\) 到这些球的交内一点的距离的最小值.保证有解.\(n\le 10^6\). 和最小圆覆盖一个套路.考虑维护一个当前 ...

  6. vue3获取数据的注意点

    场景描述 在使用vue3的时候.我们很多人喜欢一个页面分成几个几个部分来写 这样做的目的是为了区分. 做的彼此的逻辑互相独立,互不干扰 但是有的时候,我们可能会去获取不属于自己区域的函数 这个时候我们 ...

  7. vue3跟新视图遇见神奇现象

    场景描述 今天遇见一个问题, tableAllFun 函数中写了一个 index=1; 然后在 otherAllFun 函数中去改变这个index=2的值 奇怪的事情发生了 在视图index展示的值是 ...

  8. github 2fa中国认证及TOTP App

    Because of your contributions on GitHub, two-factor authentication will be required for your account ...

  9. 设计模式学习-使用go实现命令模式

    命令模式 定义 优点 缺点 适用范围 代码实现 命令模式对比策略模式 参考 命令模式 定义 命令模式(Command):将一个请求封装成一个对象,从而是你可用不同的的请求对客户进行参数化:对请求排队或 ...

  10. SqlSugar删除数据

    1.根据实体删除 1.1 强类型实体 需要配置主键 ,根据主键删除需要给实体配置主键,参考文档实体配置 //单个实体 db.Deleteable<Student>(new Student( ...