引用计数类和多线程

时间:2014-01-14 16:47:30

标签: c++ multithreading reference-counting

我是多线程编程的新手,我仍然对此感到困惑。

以下是我的参考计数类:

class Rbuffer
{
  private:
    char *m_pnData;
    volatile unsigned int mRefCount;

  public:
     Rbuffer(int nLength) : mRefCount(0)
    {
    m_pnData = new char[nLength]; 
    }
   ~Rbuffer(){
    delete[] m_pnData;
    }

   void decRef() {
     if(InterlockedDecrement(&mRefCount)==0){
               delete (Rbuffer *)this;
           }
    }

  void incRef() {
        InterlockedIncrement(&mRefCount);
    } 

}; 

它是完全线程安全的吗?你能否排除这种情况:

ThreadA                                 ThreadB
PointerToRBuffer->incRef();//mRefCount 1
switch->  
                                        PointerToRBuffer->incRef();//mRefCount 2
                                          <-switch
PointerToRBuffer->decRef();           
InterlockedDecrement(&mRefCount)//mRefCount 1 
switch->                                
                                        PointerToRBuffer->decRef();//mRefCount 0!
                                        InterlockedDecrement(&mRefCount);
                                        if (0==0)
                                        delete (Rbuffer *)this; 
                                            <-switch
if (0==0) 
//deleting object, that doesn't exist 
delete (Rbuffer *)this;
//CRASH                               

崩溃的原因可能是只有(InterlockedDecrement(&amp; mRefCount))部分是原子的,但是 if(InterlockedDecrement(&amp; mRefCount)== 0)不? 我上面的例子错了吗?

提前感谢您的意见和建议,以使我的课程完全安全。

2 个答案:

答案 0 :(得分:3)

你的分析不对;您发布的代码正确使用interlockedDecrement

这是一种安全使用

 if(InterlockedDecrement(&mRefCount)==0)
           cleanup();

..但这确实会有你描述的问题

 InterlockedDecrement(&mRefCount);

 if (mRefCount==0)
           cleanup();

然而,delete this的使用更可能是导致问题的原因。您不太可能通过此处描述的“绝对肯定100%肯定”测试: http://www.parashift.com/c++-faq-lite/delete-this.html

特别是,以下简单代码会导致混乱。

{
  RBuffer x;  // count is what... ? zero
  x.incRef(); // make count one
  x.decRef(); // make count zero, and deletes itself 
}  // now x goes out of scope, so destructor is called a second time = chaos! 

正常的“引用计数”习惯涉及“共享对象”(带有计数),以及引用共享对象的简单“引用对象”(不是C ++引用,尽管语义相似)。 “引用对象”的构造函数和析构函数负责调用共享对象上的incref/decref方法。因此,共享对象会自动计算活动“引用对象”的数量。

答案 1 :(得分:2)

它不是100%清楚发生了什么,但看起来ThreadA删除了RBuffer对象,然后ThreadB取消引用它。

你真正需要的是关于递减和删除操作的互斥,此外你需要设置某种标志以防止删除后取消引用。通常将指针设置为NULL,然后在任何取消引用之前检查NULL。

所以你的decRef可能看起来像这样:

 void decRef() {
     lock(_mutex);
     if(InterlockedDecrement(&mRefCount)==0) {
               PointerToRBuffer = NULL;
               delete (Rbuffer *)this;
           }
    }

使用shared_ptr可能会更好。