CComPtr赋值操作是否具备线程安全性?
关于CComPtr跨线程赋值的线程安全问题解答
1. 全局/静态存储的CComPtr赋值操作本身不具备线程安全性
你找到的AtlComPtrAssign正是传统ATL中CComPtr赋值运算符的底层实现,从代码逻辑就能看出,整个赋值过程没有任何同步机制保护:
///////////////////////////////////////////////////////////////////////////// // No Longer Used Smart Pointer helpers, retained for source compatibility ATLINLINE ATLAPI_(IUnknown*) AtlComPtrAssign( _Inout_opt_ _Deref_pre_maybenull_ _Deref_post_maybenull_ IUnknown** pp, _In_opt_ IUnknown* lp) { if (pp == NULL) return NULL; if (lp != NULL) lp->AddRef(); if (*pp) (*pp)->Release(); *pp = lp; return lp; }
哪怕是新版ATL的实现,默认也不会给智能指针赋值加锁,和C++标准库智能指针的设计逻辑一致:仅保证引用计数本身的原子性,赋值操作默认不做线程安全处理。
2. 你提到的崩溃场景完全可能发生
风险成立的核心前提是:你传入赋值操作的源指针lp,当前线程并没有提前持有它的有效引用计数。
最常见的错误场景是:另一个线程持有COM对象的最后一个引用,你直接从无锁共享的变量里读取到了这个对象的原始指针存到lp,刚过了if (lp != NULL)的校验,其他线程就执行了最后一次Release把对象销毁了,此时你再调用lp->AddRef就是访问野内存,直接触发崩溃。
3. 互斥锁(mutex)可以解决该问题,但需要遵守使用规范
只给赋值操作加锁是没用的,必须保证所有访问该全局/静态CComPtr的逻辑(读、写、修改)都持有同一把互斥锁,具体可以按以下规则实现:
- 任何时候要读取该全局指针使用,都要在锁内完成AddRef,把持有有效引用的
CComPtr局部变量带出锁外使用,使用结束后局部变量自动析构释放引用,避免对象在使用过程中被其他线程销毁 - 任何时候要修改该全局指针的指向(赋值、置空等),也必须持有同一把锁再操作
额外注意:以上只是保证指针访问的线程安全,COM对象本身的跨线程调用还要遵守COM单元模型的约束,如果是跨公寓调用COM接口,还需要做接口列集处理,否则依然会出现调用错误
内容的提问来源于stack exchange,提问作者user15284017
相关产品推荐
相关产品推荐

