Do std容器总是在多线程环境中抛出异常

Do std containers always throw exceptions in a multi-threaded environment?

本文关键字:多线程 环境 抛出异常 std Do      更新时间:2023-10-16

Do containers like std::vector&friends(我实际上使用的是QList)抛出了一个可捕获的异常,或者如果一个线程在另一个线程读取容器时试图写入容器,这是未定义的行为:

std::vector<std::string> stuff;

另一个线程中的非关键任务(例如拼写检查):

try {
    for (std::string& s : stuff) {
        //do stuff with s
    }
} catch (...) {  // Handle all exceptions
    //bail out of task
}

主线程:

stuff.erase(std::remove(someIterator), stuff.end()); 

所以你可以看到,在这里会有一个场景,它可能有一个无效的迭代器,并且会在读取线程中抛出一个异常——它会被捕获并退出任务。

但这只是一种情况——我是否可以依赖从这些容器抛出可捕获的异常,这样我就不需要用互斥锁来保护向量或字符串了?或者在某些情况下,它可能会取消引用nullptr(或其他什么)并导致SEH异常,即我无法捕获并继续的异常。我认为答案是它可能依赖于实现,并且很可能会导致未定义的行为,但我想我会问这个问题。

一般来说,您不能指望对无效迭代器的访问会引发任何类型的异常。结果是"未定义的行为":调用可能会抛出,可能会崩溃,可能会持续数年,然后咬你,可能会破坏程序中其他无关的东西。

标准禁止在标准库的对象和函数上做这种事情:

17.6.4.10/1:

如果从不同线程调用标准库函数可能会引入数据竞争,则程序的行为是未定义的。17.6.5.9中规定了可能发生这种情况的条件。

17.6.5.9/6:

通过调用标准库容器或字符串成员函数获得的迭代器上的操作可以访问底层容器,但不能修改它。[注意:特别是,使迭代器无效的容器操作与与该容器相关联的迭代机上的操作相冲突。--尾注]

类似地,大多数Qt函数都不是线程安全的。

如果您需要在线程之间共享数据,请保护自己免受数据竞争的影响。不要指望图书馆会为你做这件事,除非文件上说它会。

使用无效迭代器本身就是未定义的行为。它并没有像你想的那样抛出异常。所以,即使使用互斥,你的想法(除非我误解了它)也不会成功。

访问同一std::对象的数据争用也是未定义行为的来源。

AFAIK标准对std::vector等人的多线程行为只字未提。但实际上,每个人都将以最直接、最高效的方式实现,这意味着没有任何线程安全性。

MSDN记录了它们实现STL的行为。我希望主流实现之间没有差异。仅供参考MSDN文档的长话短说是,如果您在其他线程中有读取器,并且容器正在被修改,那么行为将是未定义的。