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

时间:2016-02-14 19:57:48

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

我正在查看Qt 4.8.0对QString隐式共享的实现,特别是它是如何以线程安全的方式完成的。似乎关键的想法是将引用计数保存在d->ref类型的整数QBasicAtomicInt中,该整数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; } 可以原子递增和递减。对于参考:

d->ref

假设QString对象A中A.deref()的当前值为1且调用ret != 0。一旦确定了false表达式的值(即QString),不同的执行线程就可以复制QString otherString = *Aptr;,从而增加其内部引用的可能性。 1?例如,如果第二个线程有一个指向A的指针然后执行类似deref()的操作,则会发生这种情况,这会调用复制构造函数。在那种情况下,看到false返回x match { case ThingOne => println("thing one") case ThingTwo => println("thing two") case x => println("unknown: " + x) } 的线程将释放共享内存,但第二个线程会认为它仍然有效。

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

1 个答案:

答案 0 :(得分:0)

  

一旦ret!= 0表达式的值(即   false)已经决定了一个不同的执行线程可以复制   QString,因此将其内部引用递增为1?

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

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

  

我错过了什么?只要您使用,类线程是否安全   它的界面,不要指点它们?

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

  

一旦你进行多线程服用就是这样吗?   指向这些类型对象的指针本质上容易出错   应该避免?

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