- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
问题
什么时候需要使用asio_handler_invoke
来实现仅通过包装处理程序无法完成的操作?
一个规范的示例说明需要asio_handler_invoke
的情况将是理想的。
背景
boost asio文档包含一个如何使用asio_handler_invoke
here的示例,但是我认为这不是为什么要使用调用处理程序的引人注目的示例。在该示例中,您似乎可以进行如下更改(并删除asio_handler_invoke
)并获得相同的结果:
template <typename Arg1>
void operator()(Arg1 arg1)
{
queue_.add(priority_, std::bind(handler_, arg1));
}
asio_handler_invoke
。
asio_handler_invoke
总是像
asio_handler_invoke(h, &h)
一样被调用,这似乎没有多大意义。在什么情况下,参数将不是(基本上)是同一对象的副本?
io_service::run()
,所以可能是我缺少一些来自多线程循环中的经验。
最佳答案
简而言之,包装处理程序和asio_handler_invoke
可完成两项不同的任务:
asio_handler_invoke
挂钩,以自定义处理程序上下文中其他处理程序的调用。 template <typename Handler>
struct custom_handler
{
void operator()(...); // Customize invocation of handler_.
Handler handler_;
};
// Customize invocation of Function within context of custom_handler.
template <typename Function>
void asio_handler_invoke(Function function, custom_handler* context);
// Invoke custom invocation of 'perform' within context of custom_handler.
void perform() {...}
custom_handler handler;
using boost::asio::asio_handler_invoke;
asio_handler_invoke(std::bind(&perform), &handler);
asio_handler_invoke
挂钩的主要原因是允许用户自定义应用程序可能无法直接访问的处理程序的调用策略。例如,考虑由零个或多个对中间操作的调用组成的组合操作。对于每个中间操作,将代表应用程序创建一个中间处理程序,但是应用程序无法直接访问这些处理程序。使用自定义处理程序时,
asio_handler_invoke
挂钩提供了一种在给定上下文中自定义这些中间处理程序的调用策略的方法。
documentation指出:
When asynchronous operations are composed from other asynchronous operations, all intermediate handlers should be invoked using the same method as the final handler. This is required to ensure that user-defined objects are not accessed in a way that may violate the guarantees. This [
asio_handler_invoke
] hooking function ensures that the invoked method used for the final handler is accessible at each intermediate step.
asio_handler_invoke
counting_handler
,并计算在其上下文中调用函数的次数:
template <typename Handler>
class counting_handler
{
void operator()(...)
{
// invoke handler
}
Handler handler_;
};
template <typename Function>
void asio_handler_invoke(Function function, counting_handler* context)
{
// increment counter
// invoke function
}
counting_handler handler(&handle_read);
boost::asio::async_read(socket, buffer, handler);
handle_read
函数由
counting_handler
包装。由于
counting_handler
对计数包装的处理程序被调用的次数不感兴趣,因此其
operator()
将不会增加计数,而只会调用
handle_read
。但是,
counting_handler
对在
async_read
操作中在其上下文中调用的处理程序的数量感兴趣,因此
asio_handler_invoke
中的自定义调用策略将增加一个计数。
counting_handler
类型的具体示例。
operation_counter
类提供了一种使用
counting_handler
轻松包装应用程序处理程序的方法:
namespace detail {
/// @brief counting_handler is a handler that counts the number of
/// times a handler is invoked within its context.
template <class Handler>
class counting_handler
{
public:
counting_handler(Handler handler, std::size_t& count)
: handler_(handler),
count_(count)
{}
template <class... Args>
void operator()(Args&&... args)
{
handler_(std::forward<Args>(args)...);
}
template <typename Function>
friend void asio_handler_invoke(
Function intermediate_handler,
counting_handler* my_handler)
{
++my_handler->count_;
// Support chaining custom strategies incase the wrapped handler
// has a custom strategy of its own.
using boost::asio::asio_handler_invoke;
asio_handler_invoke(intermediate_handler, &my_handler->handler_);
}
private:
Handler handler_;
std::size_t& count_;
};
} // namespace detail
/// @brief Auxiliary class used to wrap handlers that will count
/// the number of functions invoked in their context.
class operation_counter
{
public:
template <class Handler>
detail::counting_handler<Handler> wrap(Handler handler)
{
return detail::counting_handler<Handler>(handler, count_);
}
std::size_t count() { return count_; }
private:
std::size_t count_ = 0;
};
...
operation_counter counter;
boost::asio::async_read(socket, buffer, counter.wrap(&handle_read));
io_service.run();
std::cout << "Count of async_read_some operations: " <<
counter.count() << std::endl;
async_read()
组成的操作将在零个或多个中间
stream.async_read_some()
操作中实现。对于这些中间操作中的每一个,将创建并调用一个类型未指定的处理程序。如果上述
async_read()
操作是根据
2
中间
async_read_some()
操作实现的,则
counter.count()
将为
2
,并且从
counter.wrap()
返回的处理程序将被调用一次。
asio_handler_invoke
钩子(Hook),而是仅在包装的处理程序调用内增加计数,则该计数为
1
,仅反射(reflect)包装的处理程序被调用的次数:
template <class Handler>
class counting_handler
{
public:
...
template <class... Args>
void operator()(Args&&... args)
{
++count_;
handler_(std::forward<Args>(args)...);
}
// No asio_handler_invoke implemented.
};
async_accept
,
async_connect
和
async_read
),但是
async_read
操作将由
2
中间
async_read_some
操作组成:
#include <functional> // std::bind
#include <iostream> // std::cout, std::endl
#include <utility> // std::forward
#include <boost/asio.hpp>
// This example is not interested in the handlers, so provide a noop function
// that will be passed to bind to meet the handler concept requirements.
void noop() {}
namespace detail {
/// @brief counting_handler is a handler that counts the number of
/// times a handler is invoked within its context.
template <class Handler>
class counting_handler
{
public:
counting_handler(Handler handler, std::size_t& count)
: handler_(handler),
count_(count)
{}
template <class... Args>
void operator()(Args&&... args)
{
handler_(std::forward<Args>(args)...);
}
template <typename Function>
friend void asio_handler_invoke(
Function function,
counting_handler* context)
{
++context->count_;
// Support chaining custom strategies incase the wrapped handler
// has a custom strategy of its own.
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, &context->handler_);
}
private:
Handler handler_;
std::size_t& count_;
};
} // namespace detail
/// @brief Auxiliary class used to wrap handlers that will count
/// the number of functions invoked in their context.
class operation_counter
{
public:
template <class Handler>
detail::counting_handler<Handler> wrap(Handler handler)
{
return detail::counting_handler<Handler>(handler, count_);
}
std::size_t count() { return count_; }
private:
std::size_t count_ = 0;
};
int main()
{
using boost::asio::ip::tcp;
operation_counter all_operations;
// Create all I/O objects.
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
tcp::socket socket1(io_service);
tcp::socket socket2(io_service);
// Connect the sockets.
// operation 1: acceptor.async_accept
acceptor.async_accept(socket1, all_operations.wrap(std::bind(&noop)));
// operation 2: socket2.async_connect
socket2.async_connect(acceptor.local_endpoint(),
all_operations.wrap(std::bind(&noop)));
io_service.run();
io_service.reset();
// socket1 and socket2 are connected. The scenario below will:
// - write bytes to socket1.
// - initiate a composed async_read operaiton to read more bytes
// than are currently available on socket2. This will cause
// the operation to complete with multple async_read_some
// operations on socket2.
// - write more bytes to socket1.
// Write to socket1.
std::string write_buffer = "demo";
boost::asio::write(socket1, boost::asio::buffer(write_buffer));
// Guarantee socket2 has received the data.
assert(socket2.available() == write_buffer.size());
// Initiate a composed operation to more data than is immediately
// available. As some data is available, an intermediate async_read_some
// operation (operation 3) will be executed, and another async_read_some
// operation (operation 4) will eventually be initiated.
std::vector<char> read_buffer(socket2.available() + 1);
operation_counter read_only;
boost::asio::async_read(socket2, boost::asio::buffer(read_buffer),
all_operations.wrap(read_only.wrap(std::bind(&noop))));
// Write more to socket1. This will cause the async_read operation
// to be complete.
boost::asio::write(socket1, boost::asio::buffer(write_buffer));
io_service.run();
std::cout << "total operations: " << all_operations.count() << "\n"
"read operations: " << read_only.count() << std::endl;
}
total operations: 4
read operations: 2
async_read()
处理程序由包装两次的处理程序组成。首先由仅计数读取操作的
operation_counter
组成,然后由
operation_counter
包装计数所有操作的结果函子:
boost::asio::async_read(..., all_operations.wrap(read_only.wrap(...)));
counting_handler
的
asio_handler_invoke
实现以支持组合。这导致对每个
operation_counter
进行适当的计数:
template <typename Function>
void asio_handler_invoke(
Function function,
counting_handler* context)
{
++context->count_;
// Support chaining custom strategies incase the wrapped handler
// has a custom strategy of its own.
using boost::asio::asio_handler_invoke;
asio_handler_invoke(function, &context->handler_);
}
asio_handler_invoke
显式调用
function()
,则仅调用最外部包装程序的调用策略。在这种情况下,它将导致
all_operations.count()
为
4
和
read_only.count()
为
0
:
template <typename Function>
void asio_handler_invoke(
Function function,
counting_handler* context)
{
++context->count_;
function(); // No chaining.
}
asio_handler_invoke
挂钩是通过
argument-dependent lookup定位的,因此它基于确切的处理程序类型。使用不支持
asio_handler_invoke
的类型来组合处理程序将防止调用策略的链接。例如,使用
std::bind()
或
std::function
将导致调用默认的
asio_handler_invoke
,从而导致调用自定义调用策略:
// Operations will not be counted.
boost::asio::async_read(..., std::bind(all_operations.wrap(...)));
strand.wrap()
返回的未指定处理程序类型可确保由链和在返回的处理程序的上下文中调用的函数包装的初始处理程序不会同时运行。使用组合操作时,这可以满足许多I/O对象的线程安全要求,因为
strand
可用于与应用程序无法访问的这些中间操作进行同步。
io_service
时,以下代码段可能会调用未定义的行为,因为这两个组合操作的中间操作可能会同时运行,因为
std::bind()
不会调用相应的
asio_handler_hook
:
boost::asio::async_read(socket, ..., std::bind(strand.wrap(&handle_read)));
boost::asio::async_write(socket, ..., std::bind(strand.wrap(&handle_write)));
关于c++ - 何时使用 `asio_handler_invoke`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32857101/
给定以下实现异步 future.wait() 的函数(完整示例 here): template typename boost::asio::async_result::type >::type as
问题 什么时候需要使用asio_handler_invoke来实现仅通过包装处理程序无法完成的操作? 一个规范的示例说明需要asio_handler_invoke的情况将是理想的。 背景 boost
我是一名优秀的程序员,十分优秀!