C++ 为什么std::shared\u ptr::unique()不推荐使用?
C++ 为什么std::shared\u ptr::unique()不推荐使用?,c++,multithreading,shared-ptr,c++17,C++,Multithreading,Shared Ptr,C++17,std::shared_ptr::unique()的技术问题是什么,这是它在C++17中被弃用的原因 根据std::shared_ptr::unique()在C++17中作为 此函数从C++17开始就不推荐使用,因为use\u count只是多线程环境中的近似值 我理解这对于use_count()>1来说是正确的:当我持有对它的引用时,其他人可能同时放弃他的或创建一个新副本 但是如果use\u count()返回1(这是我在调用unique()时感兴趣的),那么没有其他线程可以快速更改该值,因此
std::shared_ptr::unique()
的技术问题是什么,这是它在C++17中被弃用的原因
根据std::shared_ptr::unique()在C++17中作为
此函数从C++17开始就不推荐使用,因为use\u count
只是多线程环境中的近似值
我理解这对于use_count()>1来说是正确的:当我持有对它的引用时,其他人可能同时放弃他的或创建一个新副本
但是如果use\u count()
返回1(这是我在调用unique()
时感兴趣的),那么没有其他线程可以快速更改该值,因此我希望这应该是安全的:
if (myPtr && myPtr.unique()) {
//Modify *myPtr
}
我自己搜索的结果:
我找到了这个文档:它提出了对C++17 CD注释CA14的反对意见,但我找不到该注释本身
作为替代方案,该文件建议添加一些注释,包括以下内容:
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;
};
注意:当多个线程可能影响use_count()
的返回值时,应将结果视为近似值特别是,use_count()==1
并不意味着通过先前销毁的共享\u ptr
进行的访问在任何意义上都已完成。-结束说明
我知道目前指定use\u count()
的方式可能就是这种情况(由于缺乏保证的同步),但为什么解决方案不仅仅是指定这种同步,从而使上述模式安全?如果有一个基本的限制不允许这样的同步(或者使其成本高得惊人),那么如何才能正确地实现析构函数呢
更新:
我忽略了@alexeykuzmin0和@rubenvb的明显例子,因为到目前为止,我只在其他线程本身无法访问的shared_ptr
实例上使用了unique()。因此,没有危险,那个特定的实例会以一种快速的方式被复制
我仍然很想知道CA 14到底是关于什么的,因为我相信我的unique()
的所有用例都可以工作,只要它保证与其他线程上不同共享的\u ptr
实例发生的任何事情同步。所以它对我来说仍然是一个有用的工具,但我可能忽略了一些基本的东西
为了说明我的想法,请考虑以下内容:
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;
};
类内存缓存{
公众:
内存缓存(大小)
:_缓存(大小)
{
用于(自动和ptr:\缓存){
ptr=std::使_共享();
}
}
//返回的内存块可能会传递到不同的线程,
//但是函数永远不会同时从两个线程访问
std::shared_ptr getChunk()
{
auto it=std::find_if(_cache.begin(),_cache.end(),[](auto&ptr){return ptr.unique();});
if(it!=\u cache.end()){
//以前的用户不再使用内存,因此可以将其提供给其他用户
归还它;
}否则{
返回{};
}
}
私人:
std::vector\u缓存;
};
它有什么问题吗(如果unique()
实际上会与其他副本的析构函数同步)?考虑以下代码:
// global variable
std::shared_ptr<int> s = std::make_shared<int>();
// thread 1
if (s && s.unique()) {
// modify *s
}
// thread 2
auto s2 = s;
//全局变量
std::shared_ptr s=std::make_shared();
//线程1
如果(s&s.unique()){
//修改*s
}
//线程2
自动s2=s;
这里我们有一个经典的争用条件:s2
可以(也可以不)在线程2中创建为s
的副本,而线程1在if
中
unique()==true
意味着没有人拥有指向同一内存的shared\u ptr
,但并不意味着任何其他线程无法直接或通过指针或引用访问初始shared\u ptr
。为了您的查看乐趣:
本文件包含所有NB(国家机构)对Issaquah会议的意见。CA 14内容如下:
删除对use_count()和的“仅调试”限制
shared_ptr中的unique()引入了一个bug:为了让unique()
生成一个有用且可靠的值,它需要一个synchronize子句来
确保之前通过另一个引用进行的访问对用户可见
unique()的成功调用方。当前的许多实现都使用
松弛荷载,不提供此保证,因为未说明
在标准中。对于调试/提示用法,这是可以的。没有它
规范不明确且具有误导性
我认为,通过将shared_ptr
误用为线程间同步,可以解决潜在的数据竞争问题。
它说,use\u count()
返回不可靠的refcount值,因此,unique()
成员函数在多线程处理时将无效
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);
}
intmain(){
int结果=0;
自动sp1=std::使_共享(0);//引用计数:1
//开始另一个线程
std::thread另一个线程([&result,sp2=sp1]{//refcount:1->2
结果=42;//[W]存储到结果
//[D]使sp2作用域过期,refcount:2->1
});
//执行多线程操作:
//其他线程可以同时递增/递减REF。
如果(sp1.unique()){/[U]refcount==1?
断言(result==42);//[R]从结果读取
//此[R]读取操作导致数据争用w.R.t[w]写入操作。
}
另一个线程。join();
//旁注:thread termination and join()成员函数
//have发生在关系之前,所以[W]发生在[R]之前
//并且在下面的读取操作上没有数据竞争。
断言(结果=42);
}
成员函数unique()
没有任何同步效果,[D]shared_ptr
的析构函数与[U]调用之间没有before关系