Poco RefCountedObject线程安全吗?

时间:2016-08-03 16:52:45

标签: c++ multithreading poco poco-libraries

Poco RefCountedObject提供2个接口:

    inline void RefCountedObject::duplicate() const
{
    ++_counter;
}


inline void RefCountedObject::release() const throw()
{
    try
    {
        if (--_counter == 0) delete this;
    }
    catch (...)
    {
        poco_unexpected();
    }
}

使用:

class Foundation_API RefCountedObject
    /// A base class for objects that employ
    /// reference counting based garbage collection.
    ///
    /// Reference-counted objects inhibit construction
    /// by copying and assignment.
{
public:
    RefCountedObject();
        /// Creates the RefCountedObject.
        /// The initial reference count is one.

    void duplicate() const;
        /// Increments the object's reference count.

    void release() const throw();
        /// Decrements the object's reference count
        /// and deletes the object if the count
        /// reaches zero.

    int referenceCount() const;
        /// Returns the reference count.

protected:
    virtual ~RefCountedObject();
        /// Destroys the RefCountedObject.

private:
    RefCountedObject(const RefCountedObject&);
    RefCountedObject& operator = (const RefCountedObject&);

    mutable AtomicCounter _counter;
};

请注意: 可变的AtomicCounter _counter;

我的问题是,如果我在多线程中使用RefCountedObject是否安全?

在我看来,这并不是因为只有--_ counter是原子的,但如果(--_ count)不是原子的,可能会导致对已删除对象的引用。 例如,假设我有2个线程A和B一个执行重复,另一个版本执行,执行顺序如下:

  • B开始执行释放并到达--_ counter
  • 开始执行复制并到达++ _ counter
  • 此时_counter = 1
  • B执行--_计数器并返回由if评估的结果(现在是0&&& 0)
  • B被抢占并在分支声明(if)
  • 之前停止
  • 执行_counter ++并返回对象的引用
  • B继续并使用值0评估分支语句并删除对象

我们最终得到A有一个被删除对象的引用。 即使mutable关键字强制编译器不优化_counter它也不会帮助多线程

我错过了什么吗?

2 个答案:

答案 0 :(得分:1)

严格地看待重复并单独发布,以上是正确的。

但是,如果你有两个线程,都有一个指向同一个RefCountedObject的指针,你应该在每个线程中有单独的AutoPtr实例(这意味着_counter是> 1开始),或者,如果你共享如果至少有一个线程可以更改AutoPtr(这将导致调用release()),则必须使用互斥锁保护相同的AutoPtr。毋庸置疑,在多个线程之间共享可变AutoPtr是一种灾难。

如果你通过手动调用duplicate()和release()来管理RefCountedObject(我不推荐),你应该遵循POCO的引用计数规则并且要非常小心。

因此,对于性能良好的代码,RefCountedObject和AutoPtr对于多线程使用是安全的。

答案 1 :(得分:0)

这是安全的,因为计数器基于 std::atomic AND std::atomic 默认使用 std::memory_order_seq_cst 进行操作。 如果 std::atomic 正在使用 std::memory_order_relaxed,那么实现将有问题/不安全。 缺点是 std::memory_order_seq_cst 是“慢”的。有比 Poco 更快的实现,但不那么简单,通过使用一些不同的代码和不同的内存排序命令 (https://en.cppreference.com/w/cpp/atomic/memory_order)