【问题标题】:Is Poco RefCountedObject thread safe?Poco RefCountedObject 线程安全吗?
【发布时间】:2016-08-03 16:52:44
【问题描述】:

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 是原子的,而是 if(--_count) 不是原子的,并且可能导致对已删除对象的引用。 例如假设我有 2 个线程 A 和 B 一个执行重复,另一个释放,执行顺序如下:

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

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

我错过了什么吗?

【问题讨论】:

    标签: c++ multithreading poco poco-libraries


    【解决方案1】:

    严格从单独来看重复和发布,以上是正确的。

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

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

    因此,对于表现良好的代码,RefCountedObject 与 AutoPtr 一起用于多线程是安全的。

    【讨论】:

    • 您说“在 2 个线程中分离 AutoPtr 实例”。那是怎么工作的?如果我们有 2 个单独的实例,我们将有两个单独的 refCount,那么我们如何才能在正确的时间删除对象?
    【解决方案2】:

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

    ,有比 Poco 更快的实现,但不那么简单

    【讨论】:

      猜你喜欢
      • 2012-12-24
      • 1970-01-01
      • 2011-08-02
      • 2020-08-30
      • 2020-04-15
      • 2011-07-04
      • 2014-04-26
      • 2012-11-30
      • 2010-12-30
      相关资源
      最近更新 更多