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是如何实现线程安全的引用计数和多线程控制的的更多相关文章

  1. 内存管理 垃圾回收 C语言内存分配 垃圾回收3大算法 引用计数3个缺点

    小结: 1.垃圾回收的本质:找到并回收不再被使用的内存空间: 2.标记清除方式和复制收集方式的对比: 3.复制收集方式的局部性优点: https://en.wikipedia.org/wiki/C_( ...

  2. Netty中ByteBuf的引用计数线程安全的实现原理

    原文链接 Netty中ByteBuf的引用计数线程安全的实现原理 代码仓库地址 ByteBuf 实现了ReferenceCounted 接口,实现了引用计数接口,该接口的retain(int) 方法为 ...

  3. 引用计数gc机制使用不当导致内存泄漏

    上一篇文章找同事review了一下,收到的反馈是铺垫太长了,我尽量直入正题,哈哈 最近dbd压测时发现内存泄漏,其实这个问题去年已经暴露了,参见这篇博客[压测周].当时排查不够仔细,在此检讨下.关于d ...

  4. OC基础15:内存管理和自动引用计数

    "OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.什么是ARC? (1).ARC全名为A ...

  5. Andorid Binder进程间通信---Binder本地对象,实体对象,引用对象,代理对象的引用计数

    本文參考<Android系统源码情景分析>,作者罗升阳. 一.Binder库(libbinder)代码: ~/Android/frameworks/base/libs/binder --- ...

  6. String 类的实现(3)引用计数实现String类

    我们知道在C++中动态开辟空间时是用字符new和delete的.其中使用new test[N]方式开辟空间时实际上是开辟了(N*sizeof(test)+4)字节的空间.如图示其中保存N的值主要用于析 ...

  7. C语言的引用计数与对象树

    引用计数与对象树 cheungmine 2013-12-28 0 引言 我们经常在C语言中,用指针指向一个对象(Object)的结构,也称为句柄(Handle),利用不透明指针的技术把结构数据封装成对 ...

  8. python9--内存管理 引用计数 标记清除 分代回收

     复习   文件处理 1.操作文件的三步骤 -- 打开文件:硬盘的空间被操作系统持有 | 文件对象被应用程序持续 -- 操作文件:读写操作 -- 释放文件:释放操作系统对硬盘空间的持有 2.基础的读写 ...

  9. Linux内存管理 (11)page引用计数

    专题:Linux内存管理专题 关键词:struct page._count._mapcount.PG_locked/PG_referenced/PG_active/PG_dirty等. Linux的内 ...

随机推荐

  1. wxGlade的图标,竟来自名画!

    一直用wxGlade做GUI的,今天突然发现它的图标和一副油画很像,查了下资料果然如此.  wxGlade的图标,而且图标的文件名竟然就叫做mondrian.ico 下面是蒙德里安的作品,<构图 ...

  2. UNET学习笔记3 - 网络系统的概念

    服务器和 HOST 在Unity游戏里,一个游戏一般有一个服务器和多个客户端组成,但也可以没有服务器,用某一个客户端来同时做服务器用,这种就叫Host 在Host上的客户端叫Local Client, ...

  3. Replication的犄角旮旯(一)--变更订阅端表名的应用场景

    <Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...

  4. android知识杂记(一)

    记录项目中用的零碎知识点,用以备忘. android:screenOrientation:portrait 限制横屏 activity启动状态 singleTop 只执行一次,通常用在欢迎页面 sin ...

  5. 从点击Button到弹出一个MessageBox, 背后发生了什么

    思考一个最简单的程序行为:我们的Dialog上有一个Button, 当用户用鼠标点击这个Button时, 我们弹出一个MessageBox. 这个看似简单的行为, 谁能说清楚它是如何运行起来的,背后究 ...

  6. C#入门基础二

    万物皆对象:对象是包含数据和操作的实体. 属性:名词     /  对象     \      方法:动词 ============================================== ...

  7. java 堆栈分析4

    jprofiler ,又是一款好工具... —— 不过显然,我觉得有了jvisualvm就足够了,难道它会比jvisualvm还强大很多!?? 什么时候需要它呢?它有什么特别好用的地方吗? 带来什么方 ...

  8. 爱上MVC~在Views的多级文件夹

    回到目录 在MVC里,你的控制器对应的视图一般是在Views目录,而如果希望在Views里再分几个模块文件夹默认是不允许的,我们需要做一下设置,就可以实现Views下的多次文件夹层次了,例如,我们有产 ...

  9. 开源MVCPager

    PagedList<EntityName> list = filterList.OrderBy(m => m.Exp1).Reverse().ToPagedList2(id, );/ ...

  10. cordovas禁止横屏

    cordovas禁止横屏 官网 http://cordova.apache.org/docs/en/latest/config_ref/index.html#preference 配置config.x ...