【问题标题】:Is is thread-safe to copy/modify a copy of an STL container?复制/修改 STL 容器的副本是否是线程安全的?
【发布时间】:2014-08-15 18:33:00
【问题描述】:

我有一个std::map,它将被多个线程修改,并且我有一个互斥锁来锁定这些写入。但是,有时必须在长时间运行的操作期间使用映射,并且在整个操作期间保持锁定以阻塞所有这些写入是不好的。

对此我有两个问题:

  • 在其他线程写入原始映射时对映射副本执行该操作是否线程安全?
  • 在其他线程对其进行变异时复制映射是否是线程安全的?

例如:

class Foo {
    void writeToMap(Bar &bar, Baz &baz) {
        // lock mutex
        map[bar] = baz;
        // unlock mutex
    }

    std::map<Bar, Baz> getMapCopy() {
        // lock mutex (Is this necessary?)
        std::map<Bar, Baz> copy (map);
        // unlock mutex
        return copy;
    }

    std::map<Bar, Baz> map;
};

void expensiveOperation(Foo &f) {
    std::map<Bar, Baz> mapCopy = f.getMapCopy();
    // Can I safely read mapCopy?
}

【问题讨论】:

  • 您正在返回对局部变量的引用。这不起作用。如果按值返回就OK了。

标签: c++ multithreading stl


【解决方案1】:

听起来复制操作本身不是线程安全的,问题在于复制 64 位值的原子性。您复制前 4 个字节,而后 4 个字节正在被另一个线程修改,从而导致 8 个字节不一致。

请看这里:Is copy thread-safe?

但是,如果您设法创建一致的副本,我不明白为什么不...

【讨论】:

    【解决方案2】:

    当您返回对局部变量的引用时,该代码中有undefined behavior。一旦函数返回,这个 locla 变量就会被破坏,你现在有了一个被破坏的对象的引用。

    如果你想返回一个副本,那么你必须按值返回,所以它就是例如

    std::map<Bar, Baz> getMapCopy() {
        return map;
    }
    

    如果您使用来自C++11 standard thread library 的互斥锁和锁,则不需要显式解锁,互斥锁将随着锁的破坏而解锁。

    【讨论】:

    • 糟糕,谢谢!那么如果我按值返回,我得到的副本可以在成员被修改的同时安全读取吗?
    • 太棒了。我会试一试。
    • 创建副本是线程安全的吗?创建时不需要持有锁吗?
    • @Tiki : "创建副本是线程安全的吗?" 不,您需要为此持有锁。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多