为什么不推荐使用std :: shared_ptr :: unique()? [英] Why is std::shared_ptr::unique() deprecated?

查看:130
本文介绍了为什么不推荐使用std :: shared_ptr :: unique()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

std::shared_ptr::unique()的技术问题是什么,它是C ++ 17中弃用的原因?

What is the technical problem with std::shared_ptr::unique() that is the reason for its deprecation in C++17?

根据 cppreference.com std::shared_ptr::unique()在C ++ 17中已弃用,因为

According to cppreference.com, std::shared_ptr::unique() is deprecated in C++17 as

从C ++ 17开始不推荐使用此函数,因为use_count仅是多线程环境中的近似值.

this function is deprecated as of C++17 because use_count is only an approximation in multi-threaded environment.

我理解这对于use_count() > 1是正确的:当我持有对该引用的引用时,其他人可能会同时放开他的标签或创建一个新副本.

I understand this to be true for use_count() > 1: While I'm holding a reference to it, someone else might simultaneously let go of his or create a new copy.

但是,如果use_count()返回1(这是我在调用unique()时感兴趣的内容),则没有其他线程可以以合理的方式更改该值,因此我希望这应该是安全的:

But if use_count() returns 1 (which is what I'm interested in when calling unique()) then there is no other thread that could change that value in a racy way, so I would expect that this should be safe:

if (myPtr && myPtr.unique()) {
    //Modify *myPtr
}

我找到了此文档: http://www.open- std.org/jtc1/sc22/wg21/docs/papers/2016/p0521r0.html 提出弃用,以响应 C ++ 17 CD注释CA 14 ,但我无法找不到说评论本身.

I found this document: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0521r0.html which proposes the deprecation in response to C++17 CD comment CA 14, but I couldn't find said comment itself.

作为替代方案,该论文建议添加一些注释,包括以下内容:

As an alternative, that paper proposed adding some notes including the following:

注意:当多个线程可能影响use_count()的返回值时,应将结果视为近似值. 特别地,use_count() == 1并不意味着通过先前被破坏的shared_ptr进行的访问在任何意义上均已完成. —结束说明

Note: When multiple threads can affect the return value of use_count(), the result should be treated as approximate. In particular, use_count() == 1 does not imply that accesses through a previously destroyed shared_ptr have in any sense completed. — end note

我知道当前指定use_count()的方式可能是这种情况(由于缺乏保证的同步),但是为什么不只是指定这种同步并因此使上述模式安全的解决方案呢?如果有一个根本的限制不允许这种同步(或使其代价高昂),那么如何正确实现析构函数呢?

I understand that this might be the case for the way use_count() is currently specified (due to the lack of guaranteed synchronization), but why was the resolution not just to specify such synchronization and hence make the above pattern safe? If there was a fundamental limitation that wouldn't allow such synchronization (or make it forbiddingly costly), then how is it possible to correctly implement the destructor?

我忽略了@ alexeykuzmin0和@rubenvb出现的明显情况,因为到目前为止,我只在其他线程本身无法访问的shared_ptr实例上使用了unique().因此,不存在以特定方式复制特定实例的危险.

I overlooked the obvious case presented by @alexeykuzmin0 and @rubenvb, because so far I only used unique() on instances of shared_ptr that were not accessible to other threads themselves. So there was no danger that that particular instance would get copied in a racy way.

我仍然想知道CA 14的确切含义,因为我相信我对unique()的所有用例都可以正常工作,只要能保证与其他shared_ptr实例发生的任何事情同步即可.线程.因此,对于我来说,它仍然是一个有用的工具,但是在这里我可能会忽略一些基本知识.

I still would be interested to hear what exactly CA 14 was about, because I believe that all my use cases for unique() would work as long as it is guaranteed to synchronize with whatever happens to different shared_ptr instances on other threads. So it still seems like a useful tool to me, but I might overlook something fundamental here.

为说明我的想法,请考虑以下几点:

To illustrate what I have in mind, consider the following:

class MemoryCache {
public:
    MemoryCache(size_t size)
        : _cache(size)
    {
        for (auto& ptr : _cache) {
            ptr = std::make_shared<std::array<uint8_t, 256>>();
        }
    }

    // the returned chunk of memory might be passed to a different thread(s),
    // but the function is never accessed from two threads at the same time
    std::shared_ptr<std::array<uint8_t,256>> getChunk()
    {
        auto it = std::find_if(_cache.begin(), _cache.end(), [](auto& ptr) { return ptr.unique(); });
        if (it != _cache.end()) {
            //memory is no longer used by previous user, so it can be given to someone else
            return *it;
        } else {
            return{};
        }
    }
private:
    std::vector<std::shared_ptr<std::array<uint8_t, 256>>> _cache;
};

它有什么问题吗(如果unique()实际上会与其他副本的析构函数同步)?

Is there anything wrong with it (if unique() would actually synchronize with the destructors of other copies)?

推荐答案

我认为

I think that P0521R0 solves potentially data race by misusing shared_ptr as inter-thread synchronization. It says use_count() returns unreliable refcount value, and so, unique() member function will be useless when multithreading.

int main() {
  int result = 0;
  auto sp1 = std::make_shared<int>(0);  // refcount: 1

  // Start another thread
  std::thread another_thread([&result, sp2 = sp1]{  // refcount: 1 -> 2
    result = 42;  // [W] store to result
    // [D] expire sp2 scope, and refcount: 2 -> 1
  });

  // Do multithreading stuff:
  //   Other threads may concurrently increment/decrement refcounf.

  if (sp1.unique()) {      // [U] refcount == 1?
    assert(result == 42);  // [R] read from result
    // This [R] read action cause data race w.r.t [W] write action.
  }

  another_thread.join();
  // Side note: thread termination and join() member function
  // have happens-before relationship, so [W] happens-before [R]
  // and there is no data race on following read action.
  assert(result == 42);
}

成员函数unique()没有任何同步效果,并且从[D] shared_ptr的析构函数到[U]调用unique()的过程中没有先发生关系. 因此我们不能期望关系为[W]⇒[D]⇒[U]⇒[R]和[W]⇒[R]. (⇒"表示事前关系).

The member function unique() does not have any synchronization effect and there're no happens-before relationship from [D] shared_ptr's destructor to [U] calling unique(). So we cannot expect relationship [W] ⇒ [D] ⇒ [U] ⇒ [R] and [W] ⇒ [R]. ('⇒' denotes happens-before relationship).

已编辑:我发现了两个相关的LWG问题. LWG2434. shared_ptr :: use_count()是有效的 LWG2776. shared_ptr unique()和use_count().这只是一个推测,但是WG21委员会将C ++标准库的现有实现优先考虑,因此他们将其在C ++ 1z中的行为编成代码.

EDITED: I found two related LWG Issues; LWG2434. shared_ptr::use_count() is efficient, LWG2776. shared_ptr unique() and use_count(). It is just a speculation, but WG21 Committee gives priority to the existing implementation of C++ Standard Library, so they codify its behavior in C++1z.

LWG2434 引用(强调我的意思):

LWG2434 quote (emphasis mine):

shared_ptrweak_ptr有注释,它们的use_count()可能效率不高. 这是尝试确认经过重新链接的实现(例如,可以由Loki智能指针使用). 但是,没有任何使用引用链接的shared_ptr实现,尤其是在C ++ 11意识到存在多线程之后.每个人都使用原子引用计数,因此 use_count()仅仅是原子负载.

shared_ptr and weak_ptr have Notes that their use_count() might be inefficient. This is an attempt to acknowledge reflinked implementations (which can be used by Loki smart pointers, for example). However, there aren't any shared_ptr implementations that use reflinking, especially after C++11 recognized the existence of multithreading. Everyone uses atomic refcounts, so use_count() is just an atomic load.

LWG2776 引用(强调我的意思):

LWG2776 quote (emphasis mine):

LWG 2434取消了shared_ptruse_count()unique()的仅调试"限制,从而引入了一个错误.为了使unique()产生有用和可靠的值,它需要一个syncize子句,以确保通过unique()的成功调用者可以看到通过另一个引用进行的先前访问. 当前的许多实现使用宽松的负载,并且不提供此保证,因为标准中未对此进行说明.对于调试/提示用法,还可以.没有它,规范就不清楚,并且可能会引起误解.

The removal of the "debug only" restriction for use_count() and unique() in shared_ptr by LWG 2434 introduced a bug. In order for unique() to produce a useful and reliable value, it needs a synchronize clause to ensure that prior accesses through another reference are visible to the successful caller of unique(). Many current implementations use a relaxed load, and do not provide this guarantee, since it's not stated in the standard. For debug/hint usage that was OK. Without it the specification is unclear and probably misleading.

[...]

我更愿意将use_count()指定为仅提供不可靠的实际计数提示(另一种仅表示调试的方式).还是弃用它,如JF建议的那样. 我们不能使use_count()可靠,而无需添加更多的围栏.我们真的不希望有人在等待use_count() == 2来确定另一个线程到达了那个距离.不幸的是,我认为我们目前尚无话可说,这是一个错误.

I would prefer to specify use_count() as only providing an unreliable hint of the actual count (another way of saying debug only). Or deprecate it, as JF suggested. We can't make use_count() reliable without adding substantially more fencing. We really don't want someone waiting for use_count() == 2 to determine that another thread got that far. And unfortunately, I don't think we currently say anything to make it clear that's a mistake.

这意味着use_count()通常使用memory_order_relaxed,并且没有根据use_count()指定或实现唯一性.

This would imply that use_count() normally uses memory_order_relaxed, and unique is neither specified nor implemented in terms of use_count().

这篇关于为什么不推荐使用std :: shared_ptr :: unique()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆