【问题标题】:Another question on thread safe ref counting关于线程安全引用计数的另一个问题
【发布时间】:2011-02-09 00:20:52
【问题描述】:

关于如何实现线程安全的引用计数器有很多问题。 一个常见的高度投票的答案是:“使用原子增量/减量”。 好的,这是读取和写入 refCounter 的好方法,无需其他线程在其间更改它。但是。

我的代码是:

void String::Release()
{
    if ( 0 == AtomicDecrement( &refCounter ) ) )
        delete buffer;
}

所以。我递减并安全读取 refCounter。但是,如果其他线程在我将 refCounter 与零进行比较时会增加我的 refCounter 怎么办????

我错了吗?

编辑:(示例)

String* globalString = new String(); // refCount == 1 after that.

// thread 0:
delete globalString; 
  // This invokes String::Release().
  // After AtomicDecrement() counter becomes zero. 
  // Exactly after atomic decrement current thread switches to thread 1.

// thread 1:
String myCopy = *globalString;
  // This invokes AddRef(); 
  // globalString is alive;
  // internal buffer is still not deleted but refCounter is zero;
  // We increment and switch back to thread 0 where buffer will be 
  // succefully deleted;

我错了吗?

【问题讨论】:

  • 如果另一个线程没有对对象的引用,它如何增加计数器?值 0 字面意思是“没有剩下的引用”。

标签: c++ multithreading refcounting


【解决方案1】:

小心!

仅仅保护像引用计数器这样管理更大的生命周期的变量是不够的。

我已经看到像您问题中的代码这样的代码结果非常糟糕......

在您的情况下,不仅有人可以在您比较后增加计数器,而且某些线程可以获得值为 1 的计数器,然后您减少并删除缓冲区,而另一个线程使用已删除的内存... 崩溃

my2c

【讨论】:

    【解决方案2】:

    你的例子对我来说听起来不错。

    然而,这里的问题不是原子操作,而是手动删除一个对象,然后引用一个即将被删除的对象。如果引用计数不是 1,而是 8 怎么办?

    您需要避免手动删除和使对象无效,并更好地使用一些意识到并发的智能指针实现来处理引用计数。

    每当指针检测到引用计数为零时,您需要锁定对象以避免被其他线程引用,就像 double-checked locking 用于初始化新引用一样。

    【讨论】:

    • 当没有对对象的引用时,另一个线程如何访问该对象?
    • @Jeremy,正如 Anton 在问题示例中所说,如果一个线程调用 Release() 方法,并且在 AtomicDecrement 调用之后和 delete 语句之前,另一个线程尝试获取引用,则引用计数将等于 0,但第二个线程将获得引用,在删除之前增加 refcount,删除 refcount == 1 的对象。这是一个极不可能但并非不可能的情况。
    • 谢谢大家。这实际上是使 globalString 线程安全而不是内部缓冲区的问题。因此,正如 vz0 建议的那样,我应该先锁定 globalString,然后再尝试使用它。附言String 只是一个例子,在我的真实代码中我有一些更复杂的依赖关系。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    • 1970-01-01
    • 2010-11-25
    相关资源
    最近更新 更多