在锁定状态下清除std::map与移动到临时对象之间的区别

10

我正在使用std::map,并且其中有大量的元素。如果我需要清空map,我可以直接调用clear()函数。但是这可能需要一些时间来清空,特别是在多线程环境下,在锁定状态下执行此操作可能会阻塞其他调用。为了避免调用clear(),我尝试了以下方法:

std::mutex m;
std::map<int, int> my_map; // the map which I want to clear

void func()
{
    std::map<int, int> temp_map;
    {
        std::lock_guard<std::mutex> l(m);
        temp_map = std::move(my_map);
    }
}

这将在锁的保护下将my_map移动到temp_map中,将其清空。当函数结束时,temp_map将被销毁。

这是一种更好的方法以防止长时间获取锁吗?有任何性能影响吗?


4
这是一个好主意,但在释放锁之前,我建议最后调用my_map.clear()。因为根据我所记,一个被移动过的标准容器会被置于“不确定但有效”的状态 -- 因此唯一安全的方法是调用那些没有前置条件的函数,而clear()没有前置条件,并且可以确保映射的状态是确定的。 - cdhowie
11
可以使用 map::swap 成员函数来交换非空和空的映射。 - Richard Critten
4
或者使用std::swap,因为它有适用于std::map的特化版本。 - Blastfurnace
1
同时,移动操作只需要是线性的,来源:http://en.cppreference.com/w/cpp/container/map/operator%3D - Richard Critten
1
@RichardCritten:不,move的复杂度为0,只是一个到map&&的转换。你所引用的线性复杂度是operator=(map&&),但它是目标映射大小的线性,而在OP中它只是被创建,因此是常数(0)。移动构造函数map(map&&)具有预期的常数复杂度(除非你使用分配器玩得很奇怪,当然)。 - rodrigo
显示剩余6条评论
1个回答

6

我建议使用swap而不是move。移动的对象并不能保证实际上是空的或者甚至可以使用。但是通过swap和一个新创建的对象,你可以确保结果:

void func()
{
    std::map<int, int> temp_map;
    using std::swap;
    {
        std::lock_guard<std::mutex> l(m);
        swap(my_map, temp_map);
    }
}

1
@K.Wadhwa:YW。一个简单的方法是将swap视为双倍的“移动”:您将旧地图移出并将新地图移入。 - rodrigo
是的,但移动需要线性时间,而交换是常数时间。 - kwadhwa
2
@K.Wadhwa:您可能指的是问题评论中的链接……我认为那是错误的(我在上面回复了)。简而言之,a = move(b) 在您的情况下是空映射,其大小是线性的。 - rodrigo
3
“移动过的对象并不保证实际上为空或可用。” 标准在这里不同意你的观点。[lib.types.movedfrom]: “C++标准库中定义的类型的对象可能被移动(12.8)。移动操作可以明确指定或隐式生成。除非另有规定,否则这些移动后的对象必须处于有效但未指定状态。”(强调是我的)因此,my_map.clear();足以将移动后的map返回到已知(且为空)的状态。但还是+1,因为交换方式更加简洁。 - cdhowie
@cdhowie:你说得对。我记得读过,一个移动的对象只适合销毁...也许是在旧的草稿中?无论如何,已经改正了。 - rodrigo
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接