我是否需要在返回容器副本的函数上添加锁?

时间:2012-09-25 09:01:46

标签: c++ multithreading

class Manager {
public:

    list<Employee> getEmployees() {
        // Do I need to lock here?
        return emps_;
    }
    void addEmp(Employee emp); //Here I have lock
private:
    list<Employee> emps_;
};

实例Manager在多个线程之间共享。 我是否需要向getEmployees成员函数添加锁定?

我很确定我需要一个锁,因为复制了完整的列表,所以在此期间完成的任何修改(直到复制完成)都会破坏复制操作。

我只是问这个,因为我很少有人认为没有必要锁定。

修改

由于现在需要锁定,我的问题是如何以最小的开销来做到这一点。 通过以下解决方案,您可以将列表复制两次。:

list<Employee> getEmployees() {
    pthread_mutex_lock( &mutex1 );
    list<Emp> tmp  = emps_; //Copy 1 
    pthread_mutex_unlock( &mutex1 );
    return tmp;//Copy 2
}

2 个答案:

答案 0 :(得分:12)

复制整个列表,但副本的来源可能正在修改中。 stdlib容器支持完全并发的解锁读取访问。另一方面写了......

如果您在撰写副本时正在写入此列表,则需要将其锁定。一个SWMR(Single Writer Multi Reader)锁是理想的,顺便说一下,特别是如果你有几十个甚至几百个需要复制的线程,只需偶尔写一次。即使这样,编写者请求的饥饿也是一个真正的问题,需要通过锁定类的实现来解决,但不是这里的问题范围(没有双关语意)。

关于你的更新,我认为适当时是范围释放锁的忠实粉丝,在你的情况下它们是如此。即你的互斥体周围的范围对象包装器,它在入口处锁存它,并在范围 - 出口处解锁它。

我非常确定boost :: guys有这样的东西可以轻松访问,就此而言C ++ 11也可能(由于工作安排,我还没有采取过这样的措施;没有停机时间啊)。但你想要这样的东西:

list<Employee> getEmployees() 
{
    scope_lock latch(&mtx);
    return emps_;
}

上面的scope_lock只是一个简单的类,可以在构造时锁定互斥锁,并在破坏时将其解锁。这样可以防止在复制构造中抛出的异常不会永久挂起您的互斥锁的可能性。可以将其视为利用自动销毁来解锁互斥锁。胜利RAII

我希望这是有道理的。对于像这样的小获取者来说,这样的事情是理想的。同样,包括boost在内的许多工具包很可能会内置这样的东西,如果有任何提升家伙读到这个,请牛仔起来并借给指针。上面的例子就像你可以获得的范围锁一样简单。

最后,对于SWMR,逻辑完全相同。唯一的区别是锁会更多地考虑你是否要求读或写访问。

答案 1 :(得分:-3)

在你的情况下,我会说不需要锁,至少不需要你给我们的代码。原因是整个列表都是复制,因此调用该函数的每个线程都将获得自己的列表私有副本。