简单接口框架 InterlockedDecrement 14.5.21

多线程在使用情况下得使用原子锁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




你可能感兴趣的:(简单接口框架 InterlockedDecrement 14.5.21)