ATL是如何实现线程安全的引用计数和多线程控制的
ATL是如何实现线程安全的引用计数和多线程控制的
正如标题所示,这是我经常被问到的一个问题,而每次我都从头开始给人说一次,其实说来过程理解起来的确有点复杂。
我们的每一个ATL Server Object都继承于CComObjectRootEx, 而这个类其实就是秘密最核心的地方。大家想必都知道COM技术的对象存在于套间之中,套间主要分为单线程套间和多线程套间,而套间决定了引用计数的实现方式,对于单线程套间,根本不需要保护,所以引用计数的和关键数据保护的实现相对简单,而多线程套间其引用计数和数据保护实现起来就比较讲究,所有数据都需要保护。
但是问题来了,我们如果都按照单线程套间的实现方式,显然不能满足要求,而如果完全按照多线程套间的实现方式又有些浪费。这个时候C++中的高级技巧模板技术就出场了。对于复杂的多线程实现我们可以按照一般的方式实现,而对于比较特别的单线程套间我们可以使用模板特化技术,将不必要的复杂性去掉,这样既保证了灵活性,又降低了复杂度,同时也可以去掉不必要的数据结构。下面来看看实现代码:
template <class ThreadModel>
class CComObjectRootEx : public CComObjectRootBase {
public:
typedef ThreadModel _ThreadModel;
typedef typename _ThreadModel::AutoCriticalSection _CritSec;
typedef typename _ThreadModel::AutoDeleteCriticalSection _AutoDelCritSec;
typedef CComObjectLockT<_ThreadModel> ObjectLock; ~CComObjectRootEx() {} ULONG InternalAddRef() {
ATLASSERT(m_dwRef != -1L);
return _ThreadModel::Increment(&m_dwRef);
}
ULONG InternalRelease() {
#ifdef _DEBUG
LONG nRef = _ThreadModel::Decrement(&m_dwRef);
if (nRef < -(LONG_MAX / )) {
ATLASSERT( &&
_T("Release called on a pointer that has"
" already been released"));
}
return nRef;
#else
return _ThreadModel::Decrement(&m_dwRef);
#endif
} HRESULT _AtlInitialConstruct() { return m_critsec.Init(); }
void Lock() {m_critsec.Lock();}
void Unlock() {m_critsec.Unlock();}
private:
_AutoDelCritSec m_critsec;
}; template <>
class CComObjectRootEx<CComSingleThreadModel>
: public CComObjectRootBase {
public:
typedef CComSingleThreadModel _ThreadModel;
typedef _ThreadModel::AutoCriticalSection _CritSec;
typedef _ThreadModel::AutoDeleteCriticalSection
_AutoDelCritSec;
typedef CComObjectLockT<_ThreadModel> ObjectLock; ~CComObjectRootEx() {} ULONG InternalAddRef() {
ATLASSERT(m_dwRef != -1L);
return _ThreadModel::Increment(&m_dwRef);
}
ULONG InternalRelease() {
#ifdef _DEBUG
long nRef = _ThreadModel::Decrement(&m_dwRef);
if (nRef < -(LONG_MAX / )) {
ATLASSERT( && _T("Release called on a pointer "
"that has already been released"));
}
return nRef;
#else
return _ThreadModel::Decrement(&m_dwRef);
#endif
} HRESULT _AtlInitialConstruct() { return S_OK; } void Lock() {}
void Unlock() {}
};
从代码中我们可以看出,对于特化的单线程套间实现,lock() 和unlock()是空实现,内部的critical section 成员变量也被去掉了,完全兼顾了灵活性和性能。
总结
对于Windows编程,如果不理解COM技术可能永远也理解不了微软在干什么,同样,如果不懂ATL 可能就很难写出完美的COM Server。很多人,只是写出了可以运行的代码,但是根本不知道自己在干什么,不出问题是不可能的,一旦出了问题,他写的烂代码的维护成本就会成倍增加,所以我建议用ATL 技术写COM Server的朋友,最好知道自己写的每行代码意味着什么,共勉。
ATL是如何实现线程安全的引用计数和多线程控制的的更多相关文章
- 内存管理 垃圾回收 C语言内存分配 垃圾回收3大算法 引用计数3个缺点
小结: 1.垃圾回收的本质:找到并回收不再被使用的内存空间: 2.标记清除方式和复制收集方式的对比: 3.复制收集方式的局部性优点: https://en.wikipedia.org/wiki/C_( ...
- Netty中ByteBuf的引用计数线程安全的实现原理
原文链接 Netty中ByteBuf的引用计数线程安全的实现原理 代码仓库地址 ByteBuf 实现了ReferenceCounted 接口,实现了引用计数接口,该接口的retain(int) 方法为 ...
- 引用计数gc机制使用不当导致内存泄漏
上一篇文章找同事review了一下,收到的反馈是铺垫太长了,我尽量直入正题,哈哈 最近dbd压测时发现内存泄漏,其实这个问题去年已经暴露了,参见这篇博客[压测周].当时排查不够仔细,在此检讨下.关于d ...
- OC基础15:内存管理和自动引用计数
"OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.什么是ARC? (1).ARC全名为A ...
- Andorid Binder进程间通信---Binder本地对象,实体对象,引用对象,代理对象的引用计数
本文參考<Android系统源码情景分析>,作者罗升阳. 一.Binder库(libbinder)代码: ~/Android/frameworks/base/libs/binder --- ...
- String 类的实现(3)引用计数实现String类
我们知道在C++中动态开辟空间时是用字符new和delete的.其中使用new test[N]方式开辟空间时实际上是开辟了(N*sizeof(test)+4)字节的空间.如图示其中保存N的值主要用于析 ...
- C语言的引用计数与对象树
引用计数与对象树 cheungmine 2013-12-28 0 引言 我们经常在C语言中,用指针指向一个对象(Object)的结构,也称为句柄(Handle),利用不透明指针的技术把结构数据封装成对 ...
- python9--内存管理 引用计数 标记清除 分代回收
复习 文件处理 1.操作文件的三步骤 -- 打开文件:硬盘的空间被操作系统持有 | 文件对象被应用程序持续 -- 操作文件:读写操作 -- 释放文件:释放操作系统对硬盘空间的持有 2.基础的读写 ...
- Linux内存管理 (11)page引用计数
专题:Linux内存管理专题 关键词:struct page._count._mapcount.PG_locked/PG_referenced/PG_active/PG_dirty等. Linux的内 ...
随机推荐
- art虚拟机启动问题分析
欢迎转载opendevkit文章, 文章原始地址: http://www.opendevkit.com/?e=69 Attempt to invoke native method in non-sta ...
- 高性能网站架构设计之缓存篇(5)- Redis 集群(上)
集群技术是构建高性能网站架构的重要手段,试想在网站承受高并发访问压力的同时,还需要从海量数据中查询出满足条件的数据,并快速响应,我们必然想到的是将数据进行切片,把数据根据某种规则放入多个不同的服务器节 ...
- 将SQL SERVER数据库改成MySql
(www.helpqy.com) 架构在阿里云上,最先想采用SQL SERVER,想大家都是微软家族的嘛.但是发现SQL SERVER需要的配置比较高,需要的银子也比较多,最后在纠结之下换成了MySq ...
- Visual Studio 2015 RC版官方下载(ISO)
微软Build2015开发者大会发布了下代开发套件Visual Studio 2015 RC候选版,覆盖企业版.专业版以及免费授权的Community社区版,原生支持开发通用型Windows Apps ...
- PC远程调试移动设备
我们在移动端进行前端开发时,会遇到一个让人头痛但不得不面对的问题——调试. 在 PC 机器上,我们有功能强大的 Chrome DevTools.Firebug,即便是老版本的 IE ,我们也可以安装微 ...
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
概要 前面对"独占锁"和"共享锁"有了个大致的了解:本章,我们对CountDownLatch进行学习.和ReadWriteLock.ReadLock一样,Cou ...
- MFC CString::GetBuffer() 内存数据异常
问题描述 在项目中的一个文件路径存储在CString的对象中,这个对象在函数间传递了几次,当传递出来的时候,因为要使用到字符指针,所以GetBuffer获取字符串的指针,但是通过调试,发现,CStri ...
- VUE 意淫笔记
caihg Vue.js 递归组件实现树形菜单 最近看了 Vue.js 的递归组件,实现了一个最基本的树形菜单. 项目结构: main.js 作为入口,很简单: 1 2 3 4 5 6 7 8 9 i ...
- PHP面向对象06_异常处理
oop06异常处理 2014-9-2 8:36:33 NotePad++ By jiancaigege 摘要: 1.异常处理 2.类中常用函数 异常处理 语法格式: try{ //捕获异常 }catc ...
- Mybatis入门例子
Mybatis是轻量级的持久化框架,的确上手非常快. Mybatis大体上的思路就是由一个总的config文件配置全局的信息,比如mysql连接信息等.然后再mapper中指定查询的sql,以及参数和 ...