【问题标题】:Is calling size() on a std container thread safe?在标准容器线程上调用 size() 是否安全?
【发布时间】:2020-01-21 21:30:09
【问题描述】:

我有一个使用标准列表容器的缓冲区。

一个工人正在推动一侧的元素,另一个线程正在从另一侧弹出。这两个线程在访问容器之前都使用了互斥锁。

作为查看性能的一种方式,我需要查询容器的大小。但是,如果没有必要,使用互斥锁查询大小似乎有点过头了。

问题是,有必要吗?

在调用 size() 时文档说(在数据竞赛部分下: 不访问包含的元素:同时访问或修改它们是安全的。

【问题讨论】:

  • 除非明确指定为线程安全,否则将所有内容视为线程安全。
  • 例如,vector 通常根据减去两个指针来计算大小。 a-b 中有很多空间可以中断,如果在中途调整大小时指针被换掉,那真的很糟糕。
  • 不管怎样,你仍然会有竞争条件。检查后尺寸可能会发生变化。 (无论如何你不能那样做。)
  • “任何一种”方式是什么?好吧,尺寸应该在检查后改变。这就是为什么我需要继续检查..!
  • 我在下面的回答中强调,您需要在这里关注的数据是列表 container,因为这是需要通过相互包含来保护的。您的编辑指的是 包含的元素,正如我在下面的 cmets 中解释的那样,它是一种不同的数据。

标签: c++ stl thread-safety race-condition stdlist


【解决方案1】:

问题是,有必要吗?

是的。您可以在查询其大小时将元素添加到列表中,这是未定义的行为。

规则是,如果您有多个线程访问一个共享对象,并且其中至少有一个线程写入该对象,则您必须进行同步。如果没有,您就有数据竞争,这是未定义的行为。


根据您的编辑:

不访问包含的元素:同时访问或修改它们是安全的。 表示列表中的元素不被访问或改变。这意味着您可以调用size() 而不必担心它会修改列表中的任何元素。在此之前,它有容器被访问。正是这种访问不是线程安全的。如果您在调用 size 时将元素添加到列表中,那么您得到的值是未定义的。

【讨论】:

  • 即使是整数?
  • @TotteKarlsson 对于任何不是自然线程安全的东西。唯一线程安全的标准类型是std::atomic<T>
  • 还有std::atomic_flag
  • @TotteKarlsson 你对机器翻译的直觉非常错误。 MT 从字面上打破了我们直觉上认为真实的一切。 (从语义上讲,它会破坏所有程序,但不会因正式的东西而分心。如果您遵守规则,MT 会在实践中发挥作用。)
【解决方案2】:

简短的回答是:“是”。

要确定是否需要互斥,需要查看所涉及功能的“数据竞争”规范。在这种情况下,std::listpush/pop 函数规范表明 容器修改。虽然size 函数规范说容器访问

一般来说,当一些数据被多个线程访问或修改并被至少一个线程修改时,所有的访问和修改都必须受到互斥保护。

【讨论】:

  • @TotteKarlsson “包含的元素”是指包含在容器中的元素,它是与容器本身不同的数据类别。想想列表 (1, 2, 3)。列表容器是将列表保持在一起的结构,即在这种文本情况下,括号和逗号。列表元素是数字。因此,在询问列表大小时,可以修改元素(例如将“2”更改为“4”)。由于“大小”操作不涉及元素的内容,而只涉及列表容器。
猜你喜欢
  • 1970-01-01
  • 2011-11-06
  • 2021-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-22
  • 1970-01-01
相关资源
最近更新 更多