答复
多亏了探险家的评论。我得到了答案。
我问“案例1是安全的,但案例2和案例3是不安全的,是吗?”。然而,案例1是安全的
当且仅当满足我稍后编写的约束(*1)时
.这意味着
案例1一般不安全
.
这取决于
async_func()
以下是一个不安全的案例:
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
struct object : std::enable_shared_from_this<object> {
object(boost::asio::io_context& ioc):ioc(ioc) {
std::cout << "object constructor this: " << this << std::endl;
}
template <typename Handler>
void async_func(Handler&& h) {
std::cout << "this in async_func: " << this << std::endl;
h(123); // how about here?
std::cout << "call shared_from_this in async_func: " << this << std::endl;
auto sp = shared_from_this();
std::cout << "sp->get() in async_func: " << sp.get() << std::endl;
}
template <typename Handler>
void other_async_func(Handler&& h) {
std::cout << "this in other_async_func: " << this << std::endl;
h(123); // how about here?
std::cout << "call shared_from_this in other_async_func: " << this << std::endl;
auto sp = shared_from_this();
std::cout << "sp->get() in other_async_func: " << sp.get() << std::endl;
}
boost::asio::io_context& ioc;
};
int main() {
boost::asio::io_context ioc;
auto sp_object = std::make_shared<object>(ioc);
sp_object->async_func(
[sp_object = std::move(sp_object)]
(int v) mutable { // mutable is for move
std::cout << v << std::endl;
sp_object->other_async_func(
[sp_object = std::move(sp_object)]
(int v) {
std::cout << v << std::endl;
}
);
}
);
ioc.run();
}
运行演示
https://wandbox.org/permlink/uk74ACox5EEvt14o
我考虑了为什么第一个
shared_from_this()
没关系,但第二次通话
std::bad_weak_ptr
在上面的代码中。这是因为回调处理程序是从
async_func
和
other_async_func
直接地这一举动发生了两次。所以第一层(
异步函数
)
shared_from_this
他失败了。
即使回调处理程序不是直接从异步函数调用的,在多线程情况下也是不安全的。
下面是一个不安全的代码:
#include <iostream>
#include <memory>
#include <boost/asio.hpp>
struct object : std::enable_shared_from_this<object> {
object(boost::asio::io_context& ioc):ioc(ioc) {
std::cout << "object constructor this: " << this << std::endl;
}
template <typename Handler>
void async_func(Handler&& h) {
std::cout << "this in async_func: " << this << std::endl;
ioc.post(
[this, h = std::forward<Handler>(h)] () mutable {
h(123);
sleep(1);
auto sp = shared_from_this();
std::cout << "sp->get() in async_func: " << sp.get() << std::endl;
}
);
}
template <typename Handler>
void other_async_func(Handler&& h) {
std::cout << "this in other_async_func: " << this << std::endl;
ioc.post(
[this, h = std::forward<Handler>(h)] () {
h(456);
auto sp = shared_from_this();
std::cout << "sp->get() in other_async_func: " << sp.get() << std::endl;
}
);
}
boost::asio::io_context& ioc;
};
int main() {
boost::asio::io_context ioc;
auto sp_object = std::make_shared<object>(ioc);
sp_object->async_func(
[sp_object = std::move(sp_object)]
(int v) mutable { // mutable is for move
std::cout << v << std::endl;
sp_object->other_async_func(
[sp_object = std::move(sp_object)]
(int v) {
std::cout << v << std::endl;
}
);
}
);
std::vector<std::thread> ths;
ths.reserve(2);
for (std::size_t i = 0; i != 2; ++i) {
ths.emplace_back(
[&ioc] {
ioc.run();
}
);
}
for (auto& t : ths) t.join();
}
运行演示:
https://wandbox.org/permlink/xjLZWoLdn8xL89QJ
案例1的约束是安全的
*1
然而,在案例1中,当且仅当
struct object
不希望它被共享的ptr持有,它是安全的。换句话说,只要
结构对象
不用
分享了这个
这是安全的。
另一种控制顺序的方法。(支持C++14)
当且仅当满足上述约束时,我们可以在不使用C++17序列定义的情况下控制求值序列。
它支持案例1和案例3。只需获取shared_ptr持有的指针对象的引用即可。关键的一点是,即使移动了共享的_ptr,指针对象也会被保留。因此,在移动共享_ptr之前获取指针对象的引用,然后移动共享_ptr,指针对象不受影响。
然而,这是一个例外。它直接使用共享的ptr机制。所以这会受到共享ptr移动的影响。因此它是不安全的。这就是限制的原因。
案例1
// The class of sp_object class doesn't use shared_from_this mechanism
auto sp_object = std::make_shared<object>(...);
auto& r = *sp_object;
r.async_func(
params,
[sp_object]
(boost::syste_error_code const&e, ...) {
if (e) return;
auto& r = *sp_object;
r.other_async_func(
params,
[sp_object]
(boost::syste_error_code const&e, ...) {
if (e) return;
// do some
}
);
}
);
案例3
// The class of sp_object class doesn't use shared_from_this mechanism
auto sp_object = std::make_shared<object>(...);
auto& r = *sp_object;
async_func(
r,
params,
[sp_object = std::move(sp_object)]
(boost::syste_error_code const&e, ...) mutable { // mutable is for move
if (e) return;
auto& r = *sp_object;
other_async_func(
r,
params,
[sp_object = std::move(sp_object)]
(boost::syste_error_code const&e, ...) {
if (e) return;
// do some
}
);
}
);