2016-04-05 58 views
0

参考返回值我有一个非常相似的问题已经问2012年临界区,并在C++

Critical Sections and return values in C++

我想访问一个容器线程安全的藏汉而是通过引用返回缓存的版本。

struct Container { 
    const Data& getSomeData() const { 
    EnterCriticalSection(& myCritSec); 
    if (update) { 
     cache.calulatefromcontainer();   
    } 
    // fill retobj with data from structure 
    LeaveCriticalSection(& myCritSec); 
    return cache; 
    } 

private: 
    mutable Data cache; 
}; 

问题是,“返回缓存”行不再受到保护。是否有可能通过引用返回“缓存”线程安全?

+2

返回对可修改数据的引用基本上是对线程安全容器的禁用。这里并没有多少意思。 – SergeyA

回答

0

你必须考虑你的关键部分实际上保护着什么。

在您的代码中,它看起来像myCritSec正在保护容器。但值得注意的是,它是而不是保护cache成员变量。这不是因为return cache;行,而是因为您返回对它的引用,所以它可以不受客户端代码的限制使用,而其他线程再次调用getSomeData()并对其进行修改。

一种解决方案是返回数据的副本。

另一种解决方案是,每个用于从Data获取信息的公共函数都会使用父容器的myCritSec。这种方法的问题是,这将是很容易陷入种族。例如:

class Data 
{ 
public: 
    int getA() const 
    { 
     int res; 
     EnterCriticalSection(parentCS); 
     res = getAunlocked(); 
     LeaveCriticalSection(parentCS); 
     return res; 
    } 
    int getB() const 
    { 
     int res; 
     EnterCriticalSection(parentCS); 
     res = getBunlocked(); 
     LeaveCriticalSection(parentCS); 
     return res; 
    } 
}; 

然后在用户代码:

const Data &data = container.getSomeData(); 
if (data.getA() == data.getB()) // <--- RACE!!! 
{ 
} 

由于调用getA()getB()分别锁定和解锁CS,另一个线程可能会修改只是在两者之间并创建数据竞赛状况。

+0

那么唯一真正安全的解决方案是数据的副本? –

+0

@vcduser:“唯一真正安全的解决方案”?有很多方法可以做正确的事情。例如,您可以将CS处理代码添加到容器的用户,例如,如果您可以信任它的话。或者您可以复制数据的子集,或者您可以使用写时复制技术,共享指针...... – rodrigo