【问题标题】:What is the meaning of this piece of Standardese about shared_ptr's use_count()?这篇关于shared_ptr的use_count()的Standardese是什么意思?
【发布时间】:2015-03-22 17:17:10
【问题描述】:

在尝试解决this question 中显示的问题时,我发现自己陷入了 [util.smartptr.shared]/4 中的以下句子:

[...] use_count() 的更改不反映可能引入数据竞争的修改。

我不明白我应该怎么读,我会得出什么结论。 以下是一些解释:

  • 调用use_count() 不会引入数据竞争(但这应该由该函数的const-ness 以及相应的库范围保证来保证)
  • use_count() 返回的值不受(“不反映”?)需要原子性或同步的操作结果的影响(但这些相关操作是什么?)
  • use_count() 以原子方式执行,但不会阻止 CPU 或编译器重新排序(即没有顺序一致性,但为什么不提及特定模型?)

对我来说,上面的内容似乎都不是从那句话中得出的,我在试图解释它时不知所措。

【问题讨论】:

  • 通过“调用use_count()”你是否让多个线程同时调用它,但没有以任何其他方式使用那个shared_ptr对象?
  • @BenVoigt:是的(我猜),尽管“解释”只是从那句话中提取有意义的东西的绝望尝试。我什至不知道那个“东西”是什么。
  • here 的这句话是否为您澄清了这一点:Requiring shared_ptr and weak_ptr to always synchronize the use count makes it potentially slow and is inconsistent with the general approach to leave the synchronization to the user of a facility.
  • 我认为它使用use_count()来指代实际使用计数。即,假设两个shared_ptrs ab 共享同一对象的所有权;如果线程 1 从 a 创建一个新的 shared_ptr,同时线程 2 重置 b,这不会导致数据竞争,即使这两个操作都会更改使用计数。
  • @ShafikYaghmour:它确实提供了进一步的解释(谢谢),但我仍然不明白这意味着什么或在实践中意味着什么。

标签: c++ c++11 shared-ptr language-lawyer c++14


【解决方案1】:

当前措辞源自库 issue 896,它还解决了 shared_ptr 是否应该是线程安全的问题,因为可以访问拥有相同对象的不同 shared_ptrs(特别是复制和破坏) ) 同时来自不同的线程。该讨论的结论是shared_ptr 应该是线程安全的;保证这一点的机制是假装shared_ptr 成员函数只访问shared_ptr 对象本身,而不是它的堆上控制块:

为了确定是否存在数据竞争,成员函数仅访问和修改 shared_ptrweak_ptr 对象本身,而不是它们引用的对象。

这里“他们所指的对象”是指控制块。

然而,这引发了一个问题;如果我们假设拥有相同对象的不同shared_ptrs 不访问控制块,那么use_count() 肯定不能改变吗?通过使use_count() 成为一个可以凭空产生结果的魔术函数来解决这个问题:

use_count() 中的更改不反映可能引入数据竞争的修改。

也就是说,use_count() 可以从一个调用更改为下一个调用,但这并不意味着发生了数据争用(或潜在的数据争用)。从这句话的前面的措辞来看,这可能更清楚:

[注意:这是真的,尽管这样的函数经常修改 use_count() --end note]

【讨论】:

  • @AndyProwl 好吧,可以通过对来自不同线程的相同shared_ptr 进行操作来使用这些操作引入数据竞争,但我认为我们在这里是在同一页面上。关键是可以观察到use_count() 发生了变化,而这并不意味着发生了数据竞争。
  • 我认为可以添加到这个出色答案中的一条信息是来自 [res.on.data.races] 的以下内容:“7 实现可以在线程之间共享它们自己的内部对象,如果这些对象对用户不可见,并且受到保护以防止数据竞争。” 此保证不适用于控制块,因为use_count() 表示控制块的一部分“对用户可见”,因此正在讨论的语句需要指定在线程之间共享控制块保证不会引入竞争。
  • @curiousguy 不,标准使用术语“shall”,当应用于实现时,它说明了实现“必须”做什么。您可以将我对“控制块”的使用视为“使用任何内部实现细节来实现use_count”的简写;效果不变。
  • @curiousguy,与标准库的其他部分相比,大多数必须存在但未指定的东西不需要额外的线程安全性。这是一个例外,因此需要特别指出,因为覆盖整个库的笼统措辞不足以使shared_ptr 按预期安全使用。如果您有建议更清楚地指定它,请提交缺陷报告,抱怨 SO 不会改善问题。
  • @curiousguy 好吧,成员函数本身并不能确定数据竞争的存在;这就是抽象机器的工作。为了 100% 正确,这句话可能应该插入:“[...] 成员函数应 [被认为] 访问和修改 [...]”。但大多数人都可以推断出这种事情。
【解决方案2】:

这意味着use_count() 中的代码要么是无锁的,要么是使用互斥锁来锁定临界区。换句话说,您可以从线程中调用它,而不必担心竞争条件。

【讨论】:

  • 所以,如果我理解正确的话,“不反映可能引入数据竞争的修改”只是说“以原子方式执行”或“不会导致数据竞争”的一种模糊方式? “反射修改”部分让我感到困惑。
  • @AndyProwl 是的,但如果代码实际上使用互斥锁,有些人可能会发现“原子”晦涩难懂。
  • 那么“不应导致数据竞争”呢?如果这就是他们的意思,我认为他们应该(不,他们会)那样写。我还不清楚。
  • @AndyProwl 是的,这对我来说更具可读性。标准中有很多可以用更好的措辞(而且可能永远都是)的地方。
  • @Jonathan 我希望标准中的那句话用你写的方式表达。至少对我来说,从一开始就会清楚得多。
【解决方案3】:

我认为当我们添加上一句时,意图变得更加清晰:

为了确定是否存在数据竞争,成员函数应仅访问和修改 shared_ptr 和 weak_ptr 对象本身,而不是它们引用的对象。 use_count() 中的更改不反映可能引入数据竞争的修改。

所以,最后一句只是强调与第一句相同的点。例如,如果我复制 shared_ptr,它的使用计数将增加以反映 shared_ptr 已被复制的事实——因此来自 use_count() 的结果将被更改——但这是 不允许访问(尤其是不允许修改)指针对象,因此它永远不会在使用该指针对象时引入数据竞争。

【讨论】:

  • 就像注释一样,根据@ecatmur 的回答"objects they refer to" means the control block.,而不是指针对象(例如shared_ptr<int> 指向的int)。如果这就是你的意思,抱歉误解:)
  • @AndyProwl:我认为他的回答基本上是错误的。 shared_ptr 具有指向堆上包含使用计数的块的指针和指向指针对象的指针这一事实(至少正式地)是实现细节,并且它的操作对用户基本上没有影响。如果对 shared_ptr 的操作可能会在使用用户对象时引入数据竞争,那么对用户来说重要的是。
  • 呃,你是说如果shared_ptr 在不是用户对象的东西中引入数据竞争就可以了吗? “shared_ptr 具有指向堆上的块的指针(...)这一事实(至少正式地)是一个实现细节,它的操作对用户基本上没有影响。”这就是标准位所说的:无论发生什么,都不会导致数据竞争,无论是在用户数据中还是在其他地方。
  • @R.MartinhoFernandes:不,当然不是。我是说他们试图指定接口,而不是实现。由实现者以一种不会在内部引入任何数据竞争的方式来实现它。该接口表示它不能在使用指针对象时引入任何数据竞争。
  • @R.MartinhoFernandes:是的,强调“介绍”。当然,它也不能 包含 数据竞争(这将是一个明显的错误),但这是一个完全独立的问题。这里的重点是它不能引入数据竞争——也就是说,只要您使用的指针对象不包含任何数据竞争,shared_ptr 的使用就不会不添加任何内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-01
  • 1970-01-01
  • 2013-06-22
  • 2013-08-17
  • 2011-09-04
相关资源
最近更新 更多