-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
关于sharedptr thread-safe实现的问题 #4
Comments
没有问题的,因为fetch_sub返回1,实际上说明引用计数已经是0了,fetch_sub返回的是“旧值”,相当于后置i--,知道吧。如果已经为0,那就没有任何其他人持有该指针,我是独占的,那随便delete。 |
我知道fetch_sub返回1指代旧值。但我的问题是,此处的判断和delete是两个操作,而非一个原子操作。那么这俩操作存在时间间隔,所以,我认为会出现以下的情况
那么,过程3中不应该delete因为已经有别的线程在引用 |
不会再有线程2给refcnt加1了,因为线程2要给refcnt加1,就只有在sharedptr的拷贝构造函数里。因为refcnt归零,就说明没有任何其他线程持有同样的sharedptr对象,连sharedt对象都没有,也就没有任何其他线程有机会调用sharedptr的拷贝构造函数。 |
对,我理解,引用计数+1只发生在拷贝构造函数里(如果我没看错的话)。但refcnt归零只能说明在这个时刻没有其它线程持有同样的sharedptr对象,然而执行delete有时间间隔,在此间隔可能有线程2执行拷贝构造,换句话说:
因为有过程2的存在,那么过程3就不应该发生吧? |
refcnt归零说明在这个时刻没有其它线程持有同样的sharedptr对象
既然其他线程已经没有sharedptr对象了,那么他如何调用拷贝构造函数?拷贝构造函数的需要有一个对象的情况下,才能调用的。
sharedptr(sharedptr const &that)
其他线程没有sharedptr对象,那么这个that参数哪里来?
如果你是说当前线程正在析构的sharedptr对象,同时被其他线程拷贝,那是不允许的,因为标准只保证控制块SpCounter的原子性,不保证sharedptr对象的原子性。
任何类型的对象,都不能由线程1析构的同时,线程2调用拷贝构造函数,这从来都是是未定义行为,不仅仅是sharedptr。
无法顺畅的大口呼吸,是活着的最好证明
…---原始邮件---
发件人: ***@***.***>
发送时间: 2024年9月8日(周日) 上午9:48
收件人: ***@***.***>;
抄送: ***@***.******@***.***>;
主题: Re: [parallel101/stl1weekend] 关于sharedptr thread-safe实现的问题 (Issue #4)
不会再有线程2给refcnt加1了,因为线程2要给refcnt加1,就只有在sharedptr的拷贝构造函数里。因为refcnt归零,就说明没有任何其他线程持有同样的sharedptr对象,连sharedt对象都没有,也就没有任何其他线程有机会调用sharedptr的拷贝构造函数。
对,我理解,引用计数+1只发生在拷贝构造函数里(如果我没看错的话)。但refcnt归零只能说明在这个时刻没有其它线程持有同样的sharedptr对象,然而执行delete有时间间隔,在此间隔可能有线程2执行拷贝构造,换句话说:
线程1判断1成立并将引用计数归零(整体原子操作)
线程2执行拷贝构造,故引用计数+1,也就是说此时有其它线程持有该sharedptr
线程1执行delete
因为有过程2的存在,那么过程3就不应该发生吧?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.Message ID: ***@***.***>
---原始邮件---
发件人: ***@***.***>
发送时间: 2024年9月8日(周日) 上午9:48
收件人: ***@***.***>;
抄送: ***@***.******@***.***>;
主题: Re: [parallel101/stl1weekend] 关于sharedptr thread-safe实现的问题 (Issue #4)
不会再有线程2给refcnt加1了,因为线程2要给refcnt加1,就只有在sharedptr的拷贝构造函数里。因为refcnt归零,就说明没有任何其他线程持有同样的sharedptr对象,连sharedt对象都没有,也就没有任何其他线程有机会调用sharedptr的拷贝构造函数。
对,我理解,引用计数+1只发生在拷贝构造函数里(如果我没看错的话)。但refcnt归零只能说明在这个时刻没有其它线程持有同样的sharedptr对象,然而执行delete有时间间隔,在此间隔可能有线程2执行拷贝构造,换句话说:
线程1判断1成立并将引用计数归零(整体原子操作)
线程2执行拷贝构造,故引用计数+1,也就是说此时有其它线程持有该sharedptr
线程1执行delete
因为有过程2的存在,那么过程3就不应该发生吧?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.Message ID: ***@***.***>
|
这样吧,我也听不懂你在讲什么,你来写一份你认为会产生问题的代码,让我分析,例如: shared_ptr<int> a = make_shared<int>();
void t1() {
a = nullptr; // 析构a
}
void t2() {
auto b = a; // 拷贝a到b
} |
非常感谢你,我再整理整理语言并配以代码来描述我的问题 |
我设想了如下的代码: shared_ptr<int> a = make_shared<int>();
void fun1() {
a = nullptr; // 析构a
}
void func2() {
auto b = a; // 拷贝a到b
}
int main(){
auto t1=std::thread(func1);
auto t2=std::thread(func2);
t1.join();
t2.join();
return 0;
} 在这段代码中,线程1析构a,而线程2拷贝a到b。由于多线程的缘故,我认为会出现以下的情况,线程1执行判断时 if (_M_refcnt.fetch_sub(1, std::memory_order_relaxed) == 1) 由于func2刚进入尚未执行拷贝,此时引用计数等于1还不是2,所以该判断为true。于是,线程1准备执行 void _M_decref() noexcept {
if (_M_refcnt.fetch_sub(1, std::memory_order_relaxed) == 1) {
delete this;
}
} 此处的判断和delete是两个操作,而非一个原子操作。 很抱歉之前未能及时回复 |
是的,这段代码有未定义行为! |
比如标准不要求 vector 的 clear 和 push_back 同时调用是线程安全的,那么我就不需要把 vector 实现为安全的。 |
例如,C++ 标准对 |
为什么拷贝+拷贝是安全的?我怎么没看到cppreference说?这很复杂,是另一句话里透露的通用规则,适用于所有容器,包括shared_ptr、unique_ptr、vector等全部的容器: |
那么很明显,拷贝构造函数 |
完整的多线程安全规则表: |
所以实际上sharedptr所谓的“线程安全”,只不过是拷贝+拷贝这一情况的安全和拷贝+析构不同 |
sharedptr引用计数减被封装成如下的函数
该if块先判断原始引用计数是否等于1,如果为真则进行delete。然而判断和delete是两个操作,并不是一个原子操作。是否存在这样一种情况:判断条件成立,但在delete前有其它线程给引用计数+1?此时进行delete就出错了吧
The text was updated successfully, but these errors were encountered: