我需要为返回容器副本的函数添加锁吗

Do I need to add a lock on a function that returns a copy of a container?

本文关键字:函数 添加 加锁 副本 返回      更新时间:2023-10-16
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
}

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

如果您要在可能进行复制时写入此列表,则需要将其锁定。顺便说一句,SWMR(单写多读)锁非常适合这种情况,尤其是如果你有几十个甚至数百个线程需要进行复制,只需要偶尔写入。即便如此,编写器请求的匮乏也是一个真正需要通过锁定类的实现来解决的问题,但这并不是为了解决您的问题(并非双关语)。

关于你的更新,我是范围释放锁的忠实粉丝,只要它们合适,在你的情况下也是如此。也就是说,一个围绕互斥对象的作用域对象包装器,在进入时锁存它,在退出时解除锁存它。

我非常确信这一点:男人们有这样一个很容易访问的东西,就这一点而言,C++11也可能(由于工作日程安排,我还没有冒险;没有停机时间)。但你想要这样的东西:

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

上面的scope_lock只是一个简单的类,它在构造时锁存互斥体,在销毁时解除锁存。这可以防止在副本构造中抛出的异常不会永久挂起互斥对象的真实可能性。可以将其视为利用自动销毁来解锁互斥锁。RAII获胜。

我希望这是有道理的。对于这样的小人物来说,这样的东西是理想的。同样,包括boost在内的许多工具包可能都内置了这样的东西,如果有boost的人读到这篇文章,请勇敢地伸出援手。上面的例子是一个非常理想的范围锁。

最后,对于SWMR来说,逻辑是完全相同的。唯一的区别是,锁将接受一个额外的参数,即您是请求读访问还是写访问。

在您的情况下,我想说不需要锁,至少您向我们展示的代码不需要锁。原因是整个列表都被复制了,所以每个调用该函数的线程都将获得自己的列表私有副本。