我想拥有线程安全的索引操作符,并且我附带了以下代码,它似乎有效。 除了边界检查之外,你能看到它的任何问题吗? 有没有更好的方法来做同样的事情(使用重载索引操作符,而不是get / set函数)?
class A
{
public:
A()
{
for (auto i = 0; i < 100; ++i)
{
v[i] = i + 1;
}
};
class Proxy
{
int* val;
A* parent;
public:
Proxy(int& a, A* p) : parent(p)
{
parent->z.lock();
val = &a;
};
~Proxy()
{
parent->z.unlock();
}
int operator=(int a)
{
*val = a;
return a;
};
operator int() const
{
return *val;
};
};
int operator[](int i) const
{
z.lock();
int r = v[i];
z.unlock();
return r;
}
Proxy operator[](int i)
{
return Proxy(v[i], this);
}
int v[100];
Z z; // some locking mechanism, not important
};
答案 0 :(得分:0)
由于未指定锁定机制,我认为它使用普通的互斥锁,在这种情况下,明显的问题是:
A a;
a[0] = a[1];
换句话说,很容易死锁程序。使用递归互斥锁可以避免这个问题。
另一个明显的问题是代码依赖于copy-elision,而不能保证永远不会发生。如果在返回时没有删除副本,则临时将释放锁,并且副本将再次释放它,这通常是未定义的行为。此外,访问该成员是无人看守的,并可能引入数据竞赛。要避免此问题,您应该定义一个移动构造函数,并确保移动的状态不会尝试释放互斥锁,例如:
A::Proxy::Proxy(Proxy&& other)
: val(other.val)
, parent(other.parent) {
other.parent = 0;
}
A::Proxy::~Proxy() {
if (parent){
parent->unlock();
}
}
不那么明显的问题是它不太可能导致有效的实施。