我正在用c++ 11中的std::map
编码。
我读过这个链接,它告诉我c++ 11标准保证const方法对容器的访问在不同线程中是安全的。
根据我的理解,这意味着
std::map::size()
是线程安全的,因为这个函数被声明为size_type size() const noexcept;
。
现在我想调用函数但是,std::map::find
线程安全。例如,if (mp.find(xxx) != mp.end()) {}
.find
有两个版本,一个是const
,另一个是非const
:https://en.cppreference.com/w/cpp/container/map/find.
那么我如何知道哪个版本的find
正在调用?如何强制调用const-versionfind
以获得线程安全的代码?
我知道std::map::cend()
有一个const版本,if (mp.find(xxx) != mp.cend()) {}
会像预期的那样工作吗?
你可以用std::as_const
。
if(std::as_const(mp).find(xxx)==mp.end())
{
//do your thing
}
一般规则是const
是多读器安全的,而其他操作则不是。
但是也有一些例外。基本上,返回迭代器或对现有对象的引用(没有创建对象)的非const
操作也被认为是"读取器"。操作,并且是多阅读器安全的。使用非const_iterator
返回值来修改底层数据通常会有问题,但在许多容器中,这样的修改只会在另一个操作正在访问的同一元素上引起争用。
if (map.find(foo) == map.end())
可以安全地与其他只读取map
对象的操作一起使用。
仍然有很好的理由调用const
操作。在c++17中有std::as_const
,允许
if (std::as_const(map).find(foo) == map.cend())
只调用map
上的const
方法。您可以轻松编写自己的as_const
:
template<class T>
std::add_const_t<T>& as_const( T& t ) { return t; }
(在c++11中,您需要展开add_const_t
)。std
版本增加了
template<class T>
void as_const( T const&& t ) = delete;
阻断一些相对病理的病例
…当考虑线程安全时,要意识到它是一个关系属性。两个操作相对线程安全。
在std
容器的情况下,您必须考虑操作如何读写容器本身,它读取或写入哪些元素。虽然标准更具技术性,但这将使您直观地了解允许的内容。
只读取容器并返回迭代器或元素引用的方法有时是非const
,但它们本身只读取容器。
修改或读取元素的操作通常与其他修改该元素的操作是互斥的。在矢量中,您可以自由地读取v[0] = 77;
,而另一个线程读取v[7]
而没有同步(在vector<bool>
之外)。但是当您的.push_back
容量不足时,您需要与其他每一次读取同步。
这可能是反直觉的。如果要对容器进行无同步访问,请小心,并阅读文档。直觉只是第一步。
如果mp
是const
,那么find
将是const
版本。然而,调用非const版本在线程安全方面与调用const
版本没有什么不同,如果您实际使用返回的迭代器对保存值进行修改(如果您仔细阅读您链接的答案,它包含此区别),则会出现差异。
注意,只有调用多个const
方法是线程安全的,同时从另一个线程调用非const方法是不安全的。