单例模式(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. [转帖]SQL中 join 、in 、exists 使用场景和执行效率

    https://www.jianshu.com/p/c825c9bf42c2 众所周知,在sql 中,join /in /exists 都可以用来实现,"查询A表中在(或者不在)B表中的记录 ...

  2. [转帖]比快更快的 ELK 8 安装使用指南-Elasticsearch,Kibana,Logstash

    https://juejin.cn/post/7133907643386560519 携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情 Elastic 8 ...

  3. [转帖]优化命令之sar——最牛命令

    目录 一:sar命令概述 1.1sar概述 1.2sar常用选项 1.3常用参数 二:CPU资源监控 2.1整体CPU使用统计(-u) 2.2各个CPU使用统计(-P) 2.3将CPU使用情况保存到文 ...

  4. [转帖]016 Linux 卧槽,看懂进程信息也不难嘛?top、ps

    016 Linux 卧槽,看懂进程信息也不难嘛?top.pshttps://my.oschina.net/u/3113381/blog/5455267 1 扒开看看 top 命令参数详情 Linux ...

  5. SMEE 国内最新光刻机

  6. 据说这道Go面试题90%的人都搞错了!

    [Go面试向]defer与time.sleep初探 大家好,我是阳哥,这是我们Go就业训练营小伙伴 寸铁同学 整理的一道很有意思的面试题. 知其然更要知其所以然,通过断点调试的思路带你搞清楚来龙去脉. ...

  7. OpenIM Open Source Instant Messaging Project Docker Compose Deployment Guide

    The deployment of OpenIM involves multiple components and supports various methods including source ...

  8. 21.13 Python 实现端口流量转发

    端口流量转发(Port Forwarding)是一种网络通信技术,用于将特定的网络流量从一个端口或网络地址转发到另一个端口或地址.它在网络中扮演着一个非常重要的角色,在Python语言中实现端口转发非 ...

  9. 21.11 Python 使用CRC图片去重

    使用CRC32还可实现图片去重功能,如下FindRepeatFile函数,运行后通过对所有文件做crc校验并将校验值存储至CatalogueDict字典内,接着依次提取CRC特征值并将其存储至Cata ...

  10. 3.1 DLL注入:常规远程线程注入

    动态链接库注入技术是一种特殊的技术,它允许在运行的进程中注入DLL动态链接库,从而改变目标进程的行为.DLL注入的实现方式有许多,典型的实现方式为远程线程注入,该注入方式的注入原理是利用了Window ...