我找到了一个关于使用带有弱指针的观察者线程的这里示例:
std::thread observer;
void observe(std::weak_ptr<int> wp) {
//Start observer thread
observer = std::thread([wp](){
while(true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
//Try acquiring a shared_ptr from weak_ptr
if(std::shared_ptr<int> p = wp.lock()) {
//Success
std::cout << "Observing: " << *p << "\n";
} else {
//The managed object is destroyed.
std::cout << "Stop\n";
break;
}
}
});
}但我想知道为什么要用弱指针?
通过函数参数使用共享指针的副本,观察者函数非常可能如下所示:
void observe(std::shared_ptr<int> sp) {
observer = std::thread([sp](){
while(true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
if(sp.use_count() > 1) {
//Success
std::cout << "Observing: " << *p << "\n";
} else {
//The managed object is about to be destroyed.
std::cout << "Stop\n";
break;
}
}
});
}因此,如果指向对象的计数器为1,我们可以肯定地知道,在函数返回时,dinamic对象将被删除。
发布于 2022-08-18 15:04:31
在这个问题上对shared_ptr::use_count引入的可能的数据竞争进行了有益而有趣的讨论,最初的目的是用于调试。
但是,抛开所有这些,考虑一下在您的示例中,当独立地添加第二个观察者时会发生什么。现在,使用计数永远不会降到2以下,shared_ptr也不会被删除。但薄弱的参考解决方案将继续发挥作用,不需要对潜在的种族进行深入思考。
也许在最初的设计中,从来没有考虑过第二个观察者。但这并不意味着在将来的某个时候这看起来不是个好主意
发布于 2022-08-18 16:40:55
在提供的第一个示例中,“观察者”投票(大约。)每秒钟查看观察到的对象是否已过期。这是因为wp.lock()返回一个指向未过期对象的共享指针(wp.expired()==false)。
//The managed object is destroyed.的评论并不完全准确。该对象已过期并已被销毁,或者某个线程即将或正在销毁它--但它可能不会被销毁。
所以,撇开代码不算什么不准确的地方,这是可行的。
但是,第二个示例中提出的代码假定,因为use_count()是1,所以std::shared_ptr<int> sp是“它的最后一个类”,当共享所有者在lambda函数返回时被破坏时,对象将被销毁。
一般不是这样的。可能有一个引用该对象的weak_ptr<>,如果在调用use_count()(*)之后对该对象调用lock(),那么使用计数将增加到2,并且从lambda函数返回(当线程进入终止时)不会导致对象被破坏。
这里的要点是,一旦任何weak_ptr<>从expired()返回true,该对象就会被销毁,并且没有任何有效的用途可以获得它的另一个shared_ptr<>。但是,共享指针可以从弱指针获得(如果至少保留一个共享指针)。
请参阅C++ Reference on expired()中的这篇注释,这很能说明问题(我的重点是):
如果托管对象在线程之间共享,则此函数本质上是动态的。特别是,一个错误的结果可能会在它被使用之前变得陈旧。一个真实的结果是可靠的。
但是,尽管存在shared_ptr<>,但任何weak_ptr<>::lock()都可能在对象的“锁”中成功,并创建一个新的shared_ptr<>,但是一旦对象过期,就不会/不能(以某种方式)暂缓执行。
这是“参考计数”设计的一个共同特点。唯一真正可靠的计数是0如果您不实现弱指针,您可以通过努力计数引用,并计算出是否可以说明所有的共享所有者。但是如果你确实实现了弱指针,那么游戏就结束了。
如果你决定开始计算弱指针,并在此基础上做出关于毁灭的决定,那么它们就不再是弱指针了,这是一个完全不同的游戏。
然而,请注意,关于“参考计数”的推理是一个小把戏。只需要两个观察线程(来自问题)就会陷入死锁,其中每个线程都在等待对方释放它们的所有权来销毁对象。代码的某些角落可以玩这个游戏,但它不是管理共享所有权的可靠模型。
脚注:
我强烈建议不要在调试以外的任何事情中使用use_count(),因为唯一真正有意义的返回值是0,这相当于expired()==true。
IMHO use_count()在C++标准库中是一个不常见的错误,其中“帮助调试”成员已被公开,不可避免地会导致开发人员误入歧途,并将类锁定到标准库通常没有的特定设计中。
(*)或在其他情况下未反映在返回的使用计数中,例如关于返回的计数的轻松内存障碍。
发布于 2022-08-18 14:02:53
除了use_count比weak_ptr更难正确使用之外,您的程序的行为与最初的示例不同。
在您的代码中,只要观察线程运行,线程捕获的shared_ptr将使对象保持活动。这意味着,在最坏的情况下,您将不得不等待大约1秒后,最后一个所有者死亡之前,观察到的对象被实际销毁。与之形成对比的是,在最初的示例中,您最多只能等待weak_ptr发布其副本,这既不太可能发生,也对对象的生存期有更短的预期效果。
在int的情况下,等待一秒钟毁灭可能并不是什么大不了的事。但请考虑一个更病态的例子,即观察到的对象在其析构函数中执行实质性操作,观察者线程以每天一次检查的频率运行.
https://stackoverflow.com/questions/73390021
复制相似问题