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

标签 c++ thread-safety lock-free qt4.8

我正在查看 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("lock\n"
                 "decl %0\n"
                 "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 的线程将释放共享内存,但第二个线程会认为它仍然有效。

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

最佳答案

Isn't it possible that once the value of the ret != 0 expression (i.e. false) has been decided a different thread of execution could copy the QString, therefore incrementing its internal reference to 1?

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

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

What am I missing? Is the class thread safe as long as you only use its interface and refrain from taking pointers to them?

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

Is it that once you go multi-threaded taking pointers to these types of objects is inherently error-prone and should be avoided?

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

关于c++ - QString 的隐式共享实现如何线程安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35397279/

相关文章:

c++ - 如何在另一个函数中访问在一个函数中定义和声明的数组?

c++ - 为什么 boost lockfree freelist 大小被限制为最多 65535 个对象?

c++ - 人们实际上使用什么无锁原语在c++中进行无锁音频处理?

c++ - 使用模板基类消除工厂类派生类冗余的简洁方法

c++ - 简单的 C++ 指针 : How to link a child node to parent node

multithreading - Delphi 2007, Indy - 在 TidTCPServer 线程中声明的变量在上下文中是否安全?

java - Volatile 与并发收集一起使用?

java - 如何在操作时应用条件线程安全?

c++ - 锁定自由链表插入

c++ - 如何在 _T 包装器中使用变量?