在什么时候将unique_lock与shared_mutex一起使用?

aCu*_*ria 5 c++ c++17

通常,在使用“普通”互斥锁时,您可以像中那样使用它remove1()。但是,现在有了shared_lockunique_lock,您是否应该首先使用共享锁而仅在必要时使用唯一锁?请注意,当模型不存在时remove()可能不需要unique_lock

void remove1(int id) {
    std::unique_lock<std::shared_mutex> lock(mutex_);
    for (auto it = models_.begin(); it != models_.end(); ++it)
        if ((*it)->getId() == id)
        {
            it = models_.erase(it);
            return;
        {
}

void remove2(int id) {
    std::shared_lock<std::shared_mutex> sharedLock(mutex_);
    for (auto it = models_.begin(); it != models_.end(); ++it)
        if ((*it)->getId() == id)
        {
            sharedLock.unlock();
            std::unique_lock<std::shared_mutex> uniqueLock(mutex_);
            models_.erase(it);
            return;
        }
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 3

sharedLock.unlock();
std::unique_lock<std::shared_mutex> uniqueLock(mutex_);
Run Code Online (Sandbox Code Playgroud)

仅仅因为两个操作各自是原子的,并不意味着一个操作后面跟着另一个操作就代表一个原子序列。一旦你放弃了锁,你就放弃了它。如果该互斥体保护对容器的访问,则没有什么可以阻止您拥有的迭代器失效。

您要做的就是让内部以独占模式unique_lock原子升级外部。shared_lock这在 C++17 中根本无法完成。

当然,在shared_lock离开该if块之前,您永远不会重新锁定,因此在擦除一个元素后,您就会遇到麻烦。

这忽略了这样一个事实:每当您erase从任一循环中获取元素时,您都会跳过下一个循环。返回的迭代器erase指向下一个元素,并且您的++it循环头将跳过它。这两个函数都是如此。

但无论如何,总体目的shared_mutex是允许多个读者,但只允许一个修饰符。你的整个操作实际上就是一个修改操作。它可能是有条件修改的,但从原子角度来说它是一个修改操作。您希望每个人都可以看到操作之前的列表,或者每个人都可以看到删除所有匹配元素之后的列表。当您修改列表时,任何人都不应该看到该列表。

所以它应该使用独占访问。