【问题标题】:C++ STL map/vector of vectors and thread safety向量的 C++ STL 映射/向量和线程安全
【发布时间】:2011-01-18 14:42:53
【问题描述】:

我想知道下面的代码是否会被认为是线程安全的。我认为应该是,但对引擎盖下发生的事情不太熟悉。

基本上,我在类 Foo 中有这个函数,它将在主线程上调用,并将向量作为参数,即,

void Foo::func( vector<int> v)

在 Foo 中,我还有一个私人成员,

vector< vector<int> > vecOfVec;

func 内,我只需将任何新的v 推回vecOfVec 并检查v 的大小。如果v 小于其预期大小,我想启动另一个线程,用一些已知的预定值填充v,就像这样

void Foo::func( vector<int> v)
{
    int size = v.size();
    int index = vecOfVec.size();

    vecOfVec.push_back(v);

    if (size < 1000)
    {
        boost::thread t( boost::bind( &Foo::PushBackZeros, this, vecOfVec, index) );
    }
}

Foo::PushBackZeros 顾名思义,只需用零填充 'vecOfVec[index]' 处的向量,直到其大小增长到 1000;

现在,我在vecOfVec 的任何元素上看不到任何并发读取或写入操作。显然,对整个对象有并发操作的机会,但在vecOfVec 的特定元素上永远不会有并发操作。

有人能解释一下上述是否被认为是线程安全的吗? STL映射的范围也相同吗?如果不是,请解释。干杯!

【问题讨论】:

  • 产生另一个线程调用 PushBackZeros 的目的是什么?
  • 您应该对标准库 (STL) 的线程安全做出的唯一假设是它们不是线程安全的,并且由于该假设而进行防御性编程。跨度>
  • 这只是一个理想化的例子。我只是对更一般的情况感兴趣,在这种情况下,您对 STL 容器的元素进行操作,其中元素一旦被推送到主线程上,就不会被主线程访问/读取/写入。
  • 多线程是很棘手的,没有锁就需要非常注意所有细节。这意味着您不能相信综合理想化示例的答案将适用于您的特定问题。特别是,不同的容器有不同的实现,对一个元素的操作可能会对其他元素产生影响(删除向量中的第一个元素会通过移动它们来影响所有其他元素,在向量末尾添加一个元素可能会使其增长并影响所有其他人,插入地图可以重新平衡树并影响其他线程中的查找...

标签: c++ boost stl thread-safety containers


【解决方案1】:

我认为,这不是线程安全的。如果一个线程将一个新元素推送到向量上,会导致向量的内部内存区域需要增长,会发生什么情况?推送线程可能会将内存换成更大的缓冲区,这可能导致与此操作并行读取的线程突然从无效(已处理)内存中读取。或者线程在某些插槽中看到随机值,只是因为它们同时读取将向量的内容从旧内存位置复制到新内存位置的代码。

【讨论】:

  • 哇...感谢您的快速回答!我刚刚在 2 分钟前发布了这个。我本以为这可能是一个问题,但不确定。谢谢!
  • 如果将成员推入内部向量,外部向量不会增长。 sizeof(vector) 无论有多少成员都是固定的。
  • @CashCow:我在这里指的是由向量实现内部管理的内存,用于保存向量实例的元素。 vector 实例的大小不会改变,但它管理的内存区域的大小和地址可能会发生变化。
【解决方案2】:

如果您的Foo::PushBackZerosFoo::func( vector&lt;int&gt; v) 具有相同的签名,那么它将是线程安全的,因为您将数组的副本传递给该函数。所以不会有并发访问。

【讨论】:

  • 好收获!你说的对。当然,由线程添加到向量中的元素会以这种方式丢失(至少相对于线程函数之外的世界而言)
  • 我应该澄清一下:Foo::PushBackZeros 也会访问 vecOfVec。实际上没有必要将 vecOfVec 传递给Foo::PushBackZeros。我只是把这个作为一个例子。
  • 如果您想在函数中修改该向量,请务必使用引用...
  • 别管 PushBackZeros 的签名, boost::bind 本身是按值取值的,所以你需要 boost::ref() 你的 vecOfVecs 来避免这种情况。
  • 是的,这肯定是这样做的方式,也是它的意图。不过,正如 Dirk 所建议的那样,这会导致并发。
【解决方案3】:

vecOfVec 是按值传递的,因为 boost::bind 要求您在引用参数周围放置一个 boost::ref。

很可能是线程安全的,但没有做你认为的那样。

我不知道你为什么需要传入那个参数,因为它已经在“this”中了。

要回答您想要做的原始问题:

  • 如果可以调整 vecOfVecs 的大小,则它的任何成员都可能无效,因此它不是线程安全的。
  • 将成员推入内部向量之一不会使任何其他成员向量无效,因此您可以让不同的线程同时填充不同的向量成员,只要它们都已经存在。
  • 如果您提前“保留”,那么您可以将更多成员推回您的向量,直到达到该容量并且它不会使现有成员失效。

【讨论】:

  • 是的。该参数不是必需的-我在那里的疏忽。我还在学习提升,所以我不熟悉 ref()。感谢您的提示。
  • 如果我理解正确的话 - 如果我用 vector 交换了 vector>,将 vecOfVec 中的每个 int* 重新设置为固定大小,那么我的任何操作'd inside int* 会是线程安全的吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 2012-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多