12

我知道标准库中的容器不是线程安全的。我曾经认为一个容器,比如说 type std::list,不能被多个线程同时访问(其中一些可能会修改容器)。但现在看来,它的意义远不止眼前所见;一些更微妙的东西,一些不那么明显的东西,至少对我来说是这样。

例如,考虑这个按值接受第一个参数的函数

void log(std::string msg, severity s, /*...*/) 
{
   return; //no code!
}

这是线程安全的吗?

起初,它似乎是线程安全的,因为函数体不访问共享的可修改资源,因此是线程安全的。再想一想,当我调用这样一个函数时,std::string将创建一个类型的对象,这是第一个参数,我认为这个对象的构造不是线程安全的,因为它在内部使用std::allocator,我相信不是线程安全的。因此调用这样的函数也不是线程安全的。但如果它是正确的,那么这个呢:

void f()
{
   std::string msg = "message"; //is it thread-safe? it doesn't seem so!
}

我走对了吗?我们可以在多线程程序中使用std::string(或任何内部使用的容器)吗?std::allocator

我专门将容器称为局部变量,而不是共享对象。

我搜索了谷歌,发现了许多类似的疑问,没有具体的答案。我面临与他类似的问题:

请同时考虑 C++03 和 C++11。

4

4 回答 4

7

在 C++11 中,std::allocator是线程安全的。从它的定义来看:

20.6.9.1/6:备注:通过调用获取存储::operator new(std::size_t)

并根据 的定义::operator new

18.6.1.4: and 的库版本、全局operator newandoperator delete的用户替换版本operator new以及operator deleteC 标准库函数callocmallocreallocfree不应由于来自不同线程的并发调用而引入数据竞争 (1.10)。

C++03 没有线程的概念,因此任何线程安全都是特定于实现的;您必须参考您的实现文档以查看它提供的保证(如果有)。由于您使用的是 Microsoft 的实现,因此该页面表示从多个线程写入同一类的多个容器对象是安全的,这意味着这std::allocator是线程安全的。

于 2012-03-01T18:41:39.160 回答
5

在 C++11 中,这将在以下位置为默认分配器解决:

20.6.9.1 分配器成员 [allocator.members]

除了析构函数,默认分配器的成员函数不应由于从不同线程并发调用这些成员函数而引入数据竞争(1.10)。对分配或解除分配特定存储单元的这些函数的调用应以单个总顺序发生,并且每个此类解除分配调用应在此顺序的下一次分配(如果有)之前发生。

如果要跨不同线程使用,任何用户提供的分配器都必须遵守相同的约束。

当然,对于标准的早期版本,由于没有谈论多线程,因此没有提及这一点。如果一个实现要支持多线程(就像许多或大多数那样),它将负责处理这些问题。与实现malloc()为 C 和 C++ 提供线程安全(和其他库函数)的方式类似,尽管最近的标准对此只字未提。

于 2012-03-01T18:50:51.083 回答
3

正如您可能已经想到的那样,不会有一个简单的“是”或“否”答案。但是,我认为这可能会有所帮助:

http://www.cs.huji.ac.il/~etsman/Docs/gcc-3.4-base/libstdc++/html/faq/index.html#5_6

我逐字引用:

5.6 libstdc++-v3 线程安全吗?

当满足以下所有条件时,libstdc++-v3 会努力实现线程安全:

系统的 libc 本身是线程安全的, gcc -v 报告除“single”之外的线程模型,[仅限 3.3 之前] 存在问题的体系结构的 atomicity.h 的非通用实现。

于 2012-03-01T18:52:33.680 回答
2

std::string在调用 期间复制an时log,分配器可能是线程安全的(在 C++11 中是强制性的),但副本本身不是。因此,如果在进行复制时有另一个线程改变源字符串,这不是线程安全的。

您最终可能会得到一半的字符串,因为它是突变之前的一半,另一半是突变之后的另一半,或者如果突变线程重新分配(例如通过附加新字符)或删除字符串,而副本仍然存在,您甚至可能最终访问已释放的内存发生。


哦,那个...

std::string msg = "message";

...是线程安全的,前提是您的分配器是线程安全的。

于 2012-03-01T19:53:29.440 回答