标准容器作为多线程应用程序中的局部变量

时间:2012-03-01 18:32:17

标签: c++ multithreading string vector allocator

我知道标准库中的容器不是线程安全的。我以前认为类型为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 个答案:

答案 0 :(得分:7)

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

  

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

获取存储空间

以及::operator new的定义:

  

18.6.1.4:operator newoperator delete的库版本,全局operator newoperator delete的用户替换版本以及C标准库函数{{1} },callocmallocrealloc必须   由于来自不同线程的并发调用,不会引入数据争用(1.10)。

C ++ 03没有线程概念,因此任何线程安全都是特定于实现的;您必须参考您的实施文档,看看它提供了什么保证,如果有的话。由于您正在使用Microsoft的实现,this page表示从多个线程写入同一类的多个容器对象是安全的,这意味着free是线程安全的。

答案 1 :(得分:5)

在C ++ 11中,这将针对默认分配器解决:

  

20.6.9.1分配者成员[allocator.members]

     

除了析构函数,默认分配器的成员函数   由于并发调用,不得引入数据竞争(1.10)   来自不同线程的那些成员函数。打电话给这些   分配或取消分配特定存储单元的函数   应该在一个总订单中发生,并且每个这样的解除分配调用   应在下一次分配(如果有的话)之前按此顺序发生。

如果要在不同的线程中使用,任何用户提供的分配器都必须保持相同的约束。

当然,对于该标准的早期版本,由于他们没有谈论多线程,因此没有任何说法。如果一个实现是支持多线程(尽可能多或多数),它将负责处理这些问题。类似于实现为C和C ++提供线程安全的malloc()(和其他库函数)的方式,即使最近之前的标准没有对此做出任何说明。

答案 2 :(得分: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的非泛型实现。

答案 3 :(得分:2)

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

如果变异线程重新分配(例如通过附加新字符)或删除字符串,您可能会在变异之前和之前的另一半之后结束一半的字符串,或者甚至可能最终访问解除分配的内存,而副本仍在进行中。


OTOH,...

std::string msg = "message";

...如果您的分配器是线程安全的,则是线程安全的。