多线程在使用情况下得使用原子锁InterlockedDecrement 确保他的调用次数不会被打乱
在多线程编程中,我们经常用到InterlockedDecrement , 此处有个细节问题,简单聊一下.
下面的程序用mReferenceCount来控制是否要删除自己.
void AddRef()
{InterlockedIncrement(&mReferenceCount);
}
void Release()
{
InterlockedDecrement(&mReferenceCount);
if (0 == mReferenceCount)
{
delete this;
}
}
猛的一看,好像没什么问题, 大家看Release这个函数, InterlockedDecrement是个原子操作,假设当两个线程到到达这个函数时,
mReferenceCount为2, 只有一个线程要执行InterlockedDecrement操作, 那么mReferenceCount减一为 1, 这个线程继续往下
执行,当正在比较操作时, 第二个线程也执行了InterlockedDecrement操作,那么mReferenceCount再减一为0. 好了问题出现了.
第一个线程在比较操作时, 发现mRerenceCount为0,就去执行delete操作. 当第二个线程到达比较操作时, 发现mReferenceCount
也为0, 也去执行delete操作,连续两次delete, 程序就崩溃了.
这个问题很难发现,所以写了博克, 给诸位同行提个醒.
下面是解决方案:
void Release()
{
long refCount = InterlockedDecrement(&mReferenceCount);
if (0 == refCount)
{
delete this;
}
}
我们加入了refCount局部变量,也就是说每个线程在Release函数中各自维护一个计数结果, 这样就避免了重复删除.
接口原始申明:
//定义接口
// {F1E641CD-447A-4e41-9BD8-8E49236B75A7}
static const GUID IID_IBase =
{ 0xf1e641cd, 0x447a, 0x4e41, { 0x9b, 0xd8, 0x8e, 0x49, 0x23, 0x6b, 0x75, 0xa7 } };
//这里我们自己手写一个 其实微软已经给我们定义了一个IUnknown 里面的结构也和这个差不多
interface IBase{
public:
virtual int AddRef()=0;
virtual int Release()=0;
virtual int QueryInterface(GUID iid,void**ppiIterface)=0;
};
// {CFA60C64-D93E-48fd-9B4F-4BA53383BCAE}
static const GUID IID_IMath={0xcfa60c64, 0xd93e, 0x48fd,
{0x9b,0x4f,0x4b,0xa5,0x33,0x83,0xbc,0xae}};
interface IMath:IBase{
public:
virtual int Add(int nAdd1,int nAdd2)=0;
virtual int Sub(int nSub1,int nSub2)=0;
};
// {4DF2CB94-02E2-414d-98B5-B1639C0A1371}
static const GUID IID_IMath2 =
{ 0x4df2cb94, 0x2e2, 0x414d, { 0x98, 0xb5, 0xb1, 0x63, 0x9c, 0xa, 0x13, 0x71 } };
interface IMath2:IBase{
public:
virtual int Mud(int nMud1,int nMud2)=0;
virtual int Div(int nDiv1,int nDiv2)=0;
};
#endif
接口实现:
#include "stdafx.h"
#include "math.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
<span style="white-space:pre"> </span> )
{
return TRUE;
}
class CMath:public IMath,public IMath2{
public:
CMath();
virtual int AddRef();
virtual int Release();
virtual int Add(int nAdd1,int nAdd2);
virtual int Sub(int nSub1,int nSub2);
virtual int QueryInterface(GUID iid,void**ppiIterface);
virtual int Mud(int nMud1,int nMud2);
virtual int Div(int nDiv1,int nDiv2);
public:
LONG m_nRef;
};
CMath::CMath(){
m_nRef=0;
}
int CMath::Add(int nAdd1,int nAdd2){
return (nAdd1+nAdd2);
}
int CMath::Sub(int nSub1,int nSub2){
return (nSub1-nSub2);
}
int CMath::AddRef(){
//用的次数加1
InterlockedDecrement(&m_nRef);
return m_nRef;
}
int CMath::Release(){
InterlockedDecrement(&m_nRef);
if(m_nRef==0){
//当用的人只有0个的时候 释放
delete this;
}
return m_nRef;
}
int CMath::Mud(int nMud1,int nMud2){
return (nMud1*nMud2);
}
int CMath::Div(int nDiv1,int nDiv2){
return (nDiv1/nDiv2);
}
//接口查询
int CMath::QueryInterface(GUID iid,void**ppiIterface){
//如果IID为第一个 就返回其指针
if(iid==IID_IMath){
*ppiIterface=static_cast<IMath*>(this);
//每次判断成功指针确定之后我们就AddRef加一
AddRef();
}else if(iid==IID_IMath2){
*ppiIterface=static_cast<IMath2*>(this);
AddRef();
}else if(iid==IID_IBase){
//这里不能写IBase 不然会出现混乱的转换
//因为两个类都有IBase所有他不知道应该从IMath 还是IMath2
//所以我们就给他指定第一个类
*ppiIterface=static_cast<IMath*>(this);
AddRef();
}
return 0;
}
IMath *CreateInstance(){
IMath *piMath= new CMath;
//没次使用的时候 都要AddRef 我们就在这里加1 更加方便
piMath->AddRef();
return piMath;
}
项目调用:
#include "stdafx.h"
#include "../DllIntertace/math.h"
typedef IMath *(*CREATEINTERACE)();
IMath *CreateInterace(){
//加载动态库
HMODULE hDll=(HMODULE)LoadLibrary("DllIntertace.dll");
//获取创建接口的函数
CREATEINTERACE CreateInstance=(CREATEINTERACE)GetProcAddress(hDll,"CreateInstance");
//创建接口
IMath *piMath=CreateInstance();
//返回接口
return piMath;
}
int main(int argc, char* argv[])
{
//创建接口
IMath *piMath=CreateInterace();
//使用接口
int ADD=piMath->Add(100,100);
<span style="white-space:pre"> </span>printf("%d\n",ADD);
//接口查询
IMath2 *piMath2=NULL;
//把IID_IMath2这个ID传进去识别
//如果要把IMath接口转换成IMath2接口是不可以的,组件
//不一定是C++编译的 也可能是C# JAVA都有可能
piMath->QueryInterface(IID_IMath2,(LPVOID *)&piMath2);
int Muds=piMath2->Div(100,2);
printf("%d\n",Muds);
//释放接口
//piMath->Release();
<span style="white-space:pre"> </span>return 0;
}
程序下载:http://pan.baidu.com/s/1c0CcWfu