3
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成员函数加锁吗?

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

我只是问这个,因为我几乎没有意见认为没有必要锁定。

编辑

既然现在很清楚需要 lock ,我的问题是如何以最小的开销做到这一点。通过执行以下解决方案,您可以复制列表两次。:

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

2 回答 2

12

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

如果您要在可能制作副本时写入此列表,则需要将其锁定。顺便说一句,SWMR(单写多读)锁非常适合这个,特别是如果你有几十个甚至几百个线程需要复制,只需要偶尔写入。即便如此,编写器请求的饥饿是一个真正的问题,需要通过锁定类的实现来解决,但不适用于您的问题范围(不是双关语)。

关于您的更新,我非常喜欢适当的范围释放锁,在您的情况下也是如此。即,围绕您的互斥锁的范围对象包装器在进入时将其锁定,并在范围退出时将其解锁。

我很确定 boost:: 家伙有这样的东西很容易获得,而且 C++11 也可能(由于工作日程,我还没有冒险;没有停机时间啊)。但是你想要这样的东西:

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

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

我希望这是有道理的。对于像这样的小家伙来说,这样的事情是理想的。同样,包括 boost 在内的许多工具包可能都内置了这样的东西,如果有任何 boost 家伙读到了这篇文章,请牛仔并提供指点。上面的示例与您所能获得的理想范围锁一样微不足道。

最后,对于 SWMR,逻辑完全相同。唯一的区别是锁会附加一个参数来判断你是在请求读还是写访问。

于 2012-09-25T09:06:59.353 回答
-3

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

于 2012-09-25T09:05:10.447 回答