C++:多线程和引用计数

Fir*_*cer 6 c++ multithreading reference-counting

目前我已经使用以下方法获得了一些引用计数类:

class RefCounted
{
public:
    void IncRef()
    {
        ++refCnt;
    }
    void DecRef()
    {
        if(!--refCnt)delete this;
    }
protected:
    RefCounted():refCnt(0){}
private:
    unsigned refCnt;
    //not implemented
    RefCounted(RefCounted&);
    RefCounted& operator = (RefCounted&};
};
Run Code Online (Sandbox Code Playgroud)

我还有一个处理引用计数的智能指针类,虽然它没有统一使用(例如,在一两位性能关键代码中,我最小化了IncRef和DecRef调用的数量).

template<class T>class RefCountedPtr
{
public:
    RefCountedPtr(T *p)
    :p(p)
    {
        if(p)p->IncRef();
    }
    ~RefCountedPtr()
    {
        if(p)p->DecRef();
    }
    RefCountedPtr<T>& operator = (T *newP)
    {
        if(newP)newP->IncRef();
        if(p)   p   ->DecRef();
        p = newP;
        return *this;
    }
    RefCountedPtr<T>& operator = (RefCountedPtr<T> &newP)
    {
        if(newP.p)newP.p->IncRef();
        if(p)     p     ->DecRef();
        p = newP.p;
        return *this;
    }
    T& operator *()
    {
        return *p;
    }
    T* operator ->()
    {
        return p;
    }
    //comparison operators etc and some const versions of the above...
private:
    T *p;
};
Run Code Online (Sandbox Code Playgroud)

对于类本身的一般用途,我打算使用读写器锁定系统,但是我真的不想为每一个IncRef和DecRef调用获取一个写入器锁.

我还想到了在IncRef调用之前指针可能无效的情况,请考虑:

class Texture : public RefCounted
{
public:
    //...various operations...
private:
    Texture(const std::string &file)
    {
        //...load texture from file...
        TexPool.insert(this);
    }
    virtual ~Texture()
    {
        TexPool.erase(this);
    }
    freind CreateTextureFromFile;
};
Texture *CreateTexture(const std::string &file)
{
    TexPoolIterator i = TexPool.find(file);
    if(i != TexPool.end())return *i;
    else return new Texture(file);
}
Run Code Online (Sandbox Code Playgroud)
ThreadA                                ThreadB
t = CreateTexture("ball.png");
t->IncRef();
...use t...                            t2 = CreateTexture("ball.png");//returns *t
...                                    thread suspended...
t->DecRef();//deletes t                ...
...                                    t2->IncRef();//ERROR

所以我想我需要完全改变引用计数模型,我在设计中返回后添加引用的原因是为了支持以下内容:

MyObj->GetSomething()->GetSomethingElse()->DoSomething();
Run Code Online (Sandbox Code Playgroud)

而不是必须:

SomeObject a = MyObj->GetSomething();
AnotherObject *b = a->GetSomethingElse();
b->DoSomething();
b->DecRef();
a->DecRef();
Run Code Online (Sandbox Code Playgroud)

在多线程环境中,c ++中有快速引用计数的简洁方法吗?

Edo*_* A. 16

使引用计数为原子,您将不需要任何锁定.在Windows :: InterlockedIncrement和:: InterlockedDecrement可以使用.在C++ 0x中,你有原子<>.

  • 您的API已损坏,您应该创建并以原子方式增加.你不能有创造然后增加.两者都没有. (4认同)
  • InterlockedIncrement比关键部分快得多,并且在win32 API中可用. (3认同)
  • 这不能解决比赛问题.可以使用给定的API在CreateTexture和AddRef之间删除最后一个引用. (3认同)
  • -1这真的很糟糕,虽然数字是"安全地"递增和递减,在获取指针,取消引用,然后更改值(即至少3条指令)所花费的时间内,对象可能发生了任何事情.它可能已被删除,其他一些对象现在使用该内存,您只需开始更改其中的值.更糟糕的是,您现在正在访问一个类,但将其视为一个全新的类.互锁函数非常棘手,特别是当你开始处理类似ABA问题的事情时. (2认同)

Mat*_*ice 10

除非你知道这是我刚才使用的特定瓶颈 boost::shared_ptr

它非常快,但是在分配额外控制块时会有一些额外的开销.另一方面,它有许多好处:

  • 它是便携式的
  • 它是正确的
  • 你不必浪费你的心理周期,让你有时间实际完成任务
  • 它很快
  • 它是行业标准,其他程序员会立即理解它.
  • 它强迫你使用boost,如果你不是你应该

另请注意,您可能不希望读取器/写入器锁定参考计数对象.争用很少,额外的开销将完全压倒你所拥有的任何好处.共享指针使用芯片级原子int操作实现,这明显优于普通互斥锁,后者明显快于读取器/写入器锁定.


Jer*_*ner 6

如果您不想使用boost或C++ 0X,但仍需要无锁引用计数,则可以通过在代码中包含正确的特定于平台的原子增量/原子减量程序集例程来实现.举个例子,这是我用于引用计数的AtomicCounter类; 它适用于最常见的操作系统:

https://public.msli.com/lcs/muscle/html/AtomicCounter_8h_source.html

是的,这是一个令人讨厌的#ifdefs混乱.但它确实有效.