我正在查看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;
}
假设在一个名为A的QString对象中,d->ref的当前值为1,并且调用了A.deref()。有没有可能一旦
ret != 0
表达式(即false
)的值被确定后,另一个执行线程就会复制该QString,从而将其内部引用增加1?例如,如果第二个线程有一个指向A的指针,然后执行了QString otherString = *Aptr;
, 这将调用复制构造函数。在这种情况下,看到deref()
返回false
的线程将释放共享内存,但第二个线程将认为它仍然有效。
我错过了什么?是不是一旦使用多线程,获取这些类型对象的指针就本质上容易出错,应该避免使用?只要使用其接口并避免引用它们,该类就是线程安全的吗?
deref
是被析构函数调用的,那么从悬空指针中调用将导致未定义行为。但我不知道在不同的上下文中是否会调用deref
。 - Weak to Enuma Elish