QString 的隐式共享实现如何线程安全?



我正在查看Qt 4.8.0对QString隐式共享的实现,特别是它是如何以线程安全的方式完成的。似乎关键思想是在 QBasicAtomicInt 类型的整数d->ref中保存引用计数,该整数可以原子地递增和递减。对于裁判:

inline QString::QString(const QString &other) : d(other.d)
{
  Q_ASSERT(&other != this);
  d->ref.ref();
}
inline QString::~QString()
{
  if (!d->ref.deref())
    free(d);
}
inline bool QBasicAtomicInt::deref()
{
    unsigned char ret;
    asm volatile("lockn"
                 "decl %0n"
                 "setne %1"
                 : "=m" (_q_value), "=qm" (ret)
                 : "m" (_q_value)
                 : "memory");
    return ret != 0;
}

假设QString对象 A 中 d->ref 的当前值为 1,并调用 A.deref()。难道一次ret != 0表达式的值(即 false (已经决定一个不同的执行线程可以复制QString,因此将其内部引用增加到1?例如,如果第二个线程有一个指向 A 的指针,然后执行类似 QString otherString = *Aptr; 的操作,这将调用复制构造函数,则可能会发生这种情况。在这种情况下,看到deref()返回false的线程将释放共享内存,但第二个线程会认为它仍然有效。

我错过了什么?是不是一旦你进入多线程,就要指针指向这些类型的对象,本质上很容易出错,应该避免?只要您只使用其接口并避免指向它们的指针,类线程是否安全?

难道一旦 ret != 0 表达式的值(即 false(已决定不同的执行线程可以复制 Qstring,因此将其内部引用增加到 1?

不,因为如果 d->ref.deref(( 返回 false,那么我们保证没有其他指向 d 的指针,因此任何其他线程都无法在该对象上调用 ref(((或其他任何东西(。

或者,换句话说,如果某个地方有另一个QString对象持有指向同一共享数据对象(d(的指针,那么deref((首先不会返回false,因为(d(的引用计数仍然大于零。

我错过了什么?只要您只使用 类线程是否安全 它的界面并避免指向它们?

只要每个 QString 对象一次只能由单个线程访问,QString 类就是线程安全的——也就是说,你的代码可以处理 QString 对象,就像它们没有做任何共享数据技巧一样;共享数据技巧对调用代码是透明的(除了程序使用的内存比其他情况下少的事实之外(。 当然:)(

是不是一旦你去多线程采取 指向这些类型的对象的指针本质上容易出错,并且 应该避免吗?

让两个线程同时尝试访问同一个 QString 对象 (A( 确实是不安全的,就像即使 QString 类不进行任何隐式数据共享也是如此一样。 所以这是一个禁忌(除非你使用 QMutex 或其他一些序列化机制显式序列化访问(。 相反,您应该做的是为每个线程提供自己单独的 QString 对象(并且不必担心增加内存使用量,因为隐式共享技巧可以避免这种情况(。

相关内容

  • 没有找到相关文章

最新更新