【问题标题】:Thread safety of std::map for read-only operations只读操作的 std::map 线程安全
【发布时间】:2010-12-23 04:52:36
【问题描述】:

我有一个 std::map 用于将值(字段 ID)映射到人类可读的字符串。当我的程序在任何其他线程启动之前启动时,这个映射被初始化一次,之后它就再也不会被修改了。现在,我给每个线程自己的这个(相当大的)映射副本,但这显然是对内存的低效使用,并且会减慢程序启动速度。所以我想给每个线程一个指向映射的指针,但这会引发线程安全问题。

如果我所做的只是使用以下代码从地图中读取:

std::string name;
//here N is the field id for which I want the human readable name
unsigned field_id = N; 
std::map<unsigned,std::string>::const_iterator map_it;

// fields_p is a const std::map<unsigned, std::string>* to the map concerned.
// multiple threads will share this.
map_it = fields_p->find(field_id);
if (map_it != fields_p->end())
{
    name = map_it->second;
}
else
{
    name = "";
}

这是否可行,或者从多个线程读取 std::map 是否存在问题?

注意:我目前正在使用 Visual Studio 2008,但我希望它能够在大多数主要的 STL 实现中工作。

更新:修改了 const 正确性的代码示例。

【问题讨论】:

    标签: c++ multithreading stl thread-safety stdmap


    【解决方案1】:

    用于 MS STL 实现

    Thread Safety in the C++ Standard Library

    以下线程安全规则适用于 C++ 标准库中的所有类,包括 shared_ptr,如下所述。有时会提供更强的保证——例如,标准 iostream 对象,如下所述,以及专门用于多线程的类型,如 .

    一个对象是线程安全的,可以从多个线程中读取。例如,给定一个对象 A,同时从线程 1 和线程 2 读取 A 是安全的。

    【讨论】:

      【解决方案2】:

      是的。

      查看有关 std::set 的相同问题的相关帖子:

      Is the C++ std::set thread-safe?

      【讨论】:

        【解决方案3】:

        只要您的地图保持不变,这将适用于多个线程。您使用的地图实际上是不可变的,因此任何查找实际上都会在不会更改的地图中进行查找。

        这是一个相关链接:http://www.sgi.com/tech/stl/thread_safety.html

        STL 的 SGI 实现是 线程安全仅在某种意义上 同时访问不同的 容器是安全的,同时 对共享容器的读取访问 是安全的。如果多个线程访问一个 单个容器,以及至少一个 线程可能会写,那么 用户有责任确保 线程之间的互斥 在容器访问期间。

        您属于“对共享容器的同时读取访问”类别。

        注意:这适用于 SGI 实现。您需要检查是否使用其他实现。据我所知,在似乎被广泛用作替代方案的两种实现中,STLPort 具有内置的线程安全性。不过我不知道 Apache 的实现。

        【讨论】:

        • 注意:答案仅限于 SGI STL 实现。 OP没有提到使用哪一个。
        • 我使用了 Visual Studio 2008 附带的实现,但我一直在寻找一个涵盖 std::map 的答案,或者至少在大多数实现中(如果可能的话)。
        • 查看 STL/CRL 文档(适用于 VS20008):msdn.microsoft.com/en-us/library/bb385954.aspx。在我看来,这对于 VS2008 实现来说没有理由不正确:它使用平衡二叉树以及对地图的支持。我确信他们必须在文档中对线程安全进行某种评论
        • 我不相信。仅仅因为没有人修改地图并不一定使它成为线程安全的。我们不知道对象内部的细节。引擎盖下可能会发生一些事情,例如我们不知道的缓存。 (我曾经写过一个哈希表,缓存了最近返回的键值对)
        【解决方案4】:

        应该没问题。 如果您想记录/强制执行只读行为,可以使用 const 对其的引用。

        请注意,不保证正确性(原则上,地图可以选择在调用 find 时重新平衡自身),即使您确实只使用 const 方法(一个非常不正当的实现可能会声明树是可变的)。然而,这在实践中似乎不太可能。

        【讨论】:

        • 我会考虑一个具有可变非线程安全内部错误的实现。
        • 我稍微更新了我的代码以使用对地图的 const 引用和 const_iterator
        • drhirsch,非线程安全的实现仍然可以符合标准,但质量肯定很差。
        • @Useless:不一定。展开树会在查找期间更新树,从而可以更快地查找最近访问的元素。
        • 好点,尽管理想情况下这样的映射不会提供 const 访问器,然后使其成为树可变的。另外:复活一个老话题的方法! :-)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-11
        • 2013-02-10
        • 2011-11-04
        • 1970-01-01
        • 2012-03-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多