【问题标题】:Is std::map insert thread safe?std::map 插入线程安全吗?
【发布时间】:2020-08-24 19:56:28
【问题描述】:

我知道std::map 类在两个线程的读写中是线程不安全的。但是在多个线程中插入可以吗?

void writeMap()
{ 
    for (int i = 0; i < 1000; i++)
    {
        long long random_variable = (std::rand()) % 1000;
        std::cout << "Thread ID -> " << std::this_thread::get_id() << " with looping index " << i << std::endl;
        k1map.insert(std::make_pair(i, new p(i)));
    }
}

int main()
{
    std::srand((int)std::time(0)); 
    for (int i = 0; i < 1000; ++i)
    {
        long long random_variable = (std::rand()) % 1000;
        std::thread t(writeMap);
        std::cout << "Thread created " << t.get_id() << std::endl;
        t.detach();
    }
    return 0;
}

无论我尝试多少次,就像这样的代码运行正常。

【问题讨论】:

  • 一般来说,没有。从纯多线程的角度来看,没有对象可以保护自己免受两个线程的无序/并发访问,并且该逻辑扩展到意味着可以修改对象的对象的非静态成员在被多个线程直接使用时无法保护该对象线程。这就是为什么调用代码(在您的情况下,调用 std::map::insert() 的代码)必须处理同步(使用互斥锁等) - 除非以这种方式受到保护,否则被调用函数不能提供绝对保证。
  • 一般规则是写入不能与任何其他读取或写入同时发生。所以两个写不能并发,需要同步。 std::map::insert 是一个写操作,因为它(可能)修改了映射。
  • 理论上是这样,但是为什么实际代码运行没有任何问题,请解释
  • 当行为未定义时,对发生的事情没有明确的约束。一个完全可能的结果是,在您的测试中,发生的事情显示为“运行没有问题”。同样可能的结果是它通过了您的测试,但在客户使用时出现故障。
  • 运气好。它可能是 std 库实现的编码和/或 CPU 架构的组合和/或当两个线程在同一个内核上运行时不会出现一些并发问题,但是如果突然将它们安排到不同的内核上,那么错误可能会浮出水面和/或等等......

标签: c++ c++11 stl thread-safety


【解决方案1】:

不,std::map::insert 不是线程安全的。

大多数标准库类型只有在单独的线程中使用单独的对象实例时才是线程安全的。看看容器的docs的线程安全部分。

【讨论】:

  • const 函数不会修改对象。这并不意味着如果另一个线程修改了对象,它们可以被认为是“线程安全的”。如果在同一张地图上同时调用std::map::size()std::map::insert(),但线程不同,那么它们需要同步。
  • @Peter 理论上,只有std::map::insert 需要同步,但如果你真的想确定,保护std::map::size 也没有什么坏处
  • 不是真的,胡桃夹子。 std::map::size() 访问对象的状态。 std::map::insert() 修改对象的状态。如果它们被不同的线程调用,在同一个对象上,调用线程需要确保调用是同步的。
  • -1 表示最后一段。 std::map::size 不提供任何线程安全。 @彼得是对的。就像你有一个int,你用一个线程修改它,同时在另一个线程中不断地显示它。 两者必须同步,而不仅仅是写入int的那个。 const 方法,即使它不更改任何 mutable 字段,根据定义也不是线程安全的。
  • @Fureeish 为什么标准然后说:所有 const 成员函数都可以由同一容器上的不同线程同时调用。请参阅我在答案中提供的链接.
【解决方案2】:

程序很复杂,在某种程度上,就像魔术一样(LOL)。 各种IDE的代码运行结果不同。 之前用过VS2013,一直都是对的。 但是在vs19和linux上,同样代码的结果是错误的。 可能在vs2013上,MAP的实现有特殊的方式。

【讨论】:

    【解决方案3】:

    正如@NutCracker 所提到的,std::map::insert 不是线程安全的。

    但是,如果发布的代码工作正常,我认为原因是地图被一个线程填充得非常快,因此其他线程不再修改地图。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      • 2017-05-08
      • 1970-01-01
      相关资源
      最近更新 更多