- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
以下示例在没有断言的情况下完成:
#include <cassert>
#include <functional>
#include <future>
#include <thread>
#include <boost/asio.hpp>
class example1
{
public:
typedef boost::asio::io_context io_context;
typedef boost::asio::io_context::executor_type executor_type;
typedef boost::asio::strand<executor_type> strand;
typedef boost::asio::executor_work_guard<executor_type> work_guard;
typedef std::function<void()> handler;
example1()
: work_(boost::asio::make_work_guard(context_)),
thread_([this]() { context_.run(); }),
strand1_(context_.get_executor()),
strand2_(context_.get_executor())
{
}
~example1()
{
assert(result_.get_future().get());
work_.reset();
thread_.join();
}
void invoke()
{
handler handle = boost::asio::bind_executor(strand2_,
std::bind(&example1::strand2_handler, this));
boost::asio::post(strand1_,
std::bind(&example1::strand1_handler, this, handle));
}
void strand1_handler(handler handle)
{
assert(strand1_.running_in_this_thread());
handle();
}
void strand2_handler()
{
assert(strand1_.running_in_this_thread());
////assert(strand2_.running_in_this_thread());
result_.set_value(true);
}
private:
io_context context_;
work_guard work_;
std::thread thread_;
strand strand1_;
strand strand2_;
std::promise<bool> result_;
};
int main()
{
example1 test{};
test.invoke();
}
但是我的期望是被注释掉的断言应该成功,而不是直接在它上面的断言。根据 strand::running_in_this_thread()
,处理程序 handle
已在调用者的链中被调用,而不是提供给 bind_executor
。
我可以使用中间方法解决这个问题,如下所示。
class example2
{
public:
typedef boost::asio::io_context io_context;
typedef boost::asio::io_context::executor_type executor_type;
typedef boost::asio::strand<executor_type> strand;
typedef boost::asio::executor_work_guard<executor_type> work_guard;
typedef std::function<void()> handler;
example2()
: work_(boost::asio::make_work_guard(context_)),
thread_([this]() { context_.run(); }),
strand1_(context_.get_executor()),
strand2_(context_.get_executor())
{
}
~example2()
{
assert(result_.get_future().get());
work_.reset();
thread_.join();
}
void invoke()
{
handler handle =
std::bind(&example2::do_strand2_handler, this);
boost::asio::post(strand1_,
std::bind(&example2::strand1_handler, this, handle));
}
void strand1_handler(handler handle)
{
assert(strand1_.running_in_this_thread());
handle();
}
// Do the job of bind_executor.
void do_strand2_handler()
{
boost::asio::post(strand2_,
std::bind(&example2::strand2_handler, this));
}
void strand2_handler()
{
////assert(strand1_.running_in_this_thread());
assert(strand2_.running_in_this_thread());
result_.set_value(true);
}
private:
io_context context_;
work_guard work_;
std::thread thread_;
strand strand1_;
strand strand2_;
std::promise<bool> result_;
};
int main()
{
example2 test2{};
test2.invoke();
}
但避免这种情况大概是绑定(bind)执行器
。这是一个 boost 错误还是我遗漏了什么?我已经尝试通过 boost::asio 源代码进行跟踪,但无济于事。
更新
感谢@sehe 提供的大量帮助。上述问题可以通过多种方式解决,例如:
class example3
{
public:
typedef boost::asio::io_context io_context;
typedef boost::asio::io_context::executor_type executor_type;
typedef boost::asio::strand<executor_type> strand;
typedef boost::asio::executor_work_guard<executor_type> work_guard;
typedef boost::asio::executor_binder<std::function<void()>,
boost::asio::any_io_executor> handler;
example3()
: work_(boost::asio::make_work_guard(context_)),
thread_([this]() { context_.run(); }),
strand1_(context_.get_executor()),
strand2_(context_.get_executor())
{
}
~example3()
{
assert(result_.get_future().get());
work_.reset();
thread_.join();
}
void invoke()
{
auto handle = boost::asio::bind_executor(strand2_,
std::bind(&example3::strand2_handler, this));
boost::asio::post(strand1_,
std::bind(&example3::strand1_handler, this, handle));
}
void strand1_handler(handler handle)
{
assert(strand1_.running_in_this_thread());
boost::asio::dispatch(handle);
}
void strand2_handler()
{
assert(strand2_.running_in_this_thread());
result_.set_value(true);
}
private:
io_context context_;
work_guard work_;
std::thread thread_;
strand strand1_;
strand strand2_;
std::promise<bool> result_;
};
int main
{
example3 test3{};
test3.invoke();
}
最佳答案
是的,您确实遗漏了一些东西。实际上有两件事。
绑定(bind)一个执行器不会修改函数,它会修改它的类型。
但是,通过使用 std::function<>
删除可调用对象的类型你已经隐藏了绑定(bind)的执行者。你可以很容易地确定这一点:
erased_handler handle = bind_executor(s2, s2_handler);
assert(asio::get_associated_executor(handle, s1) == s1);
保留类型后问题就消失了:
auto handle = bind_executor(s2, s2_handler);
assert(asio::get_associated_executor(handle, s1) == s2);
handler_invoke
)调用 handle
正如您所发现的,直接根据 C++ 语言语义调用它。
要让 Asio 尊重潜在绑定(bind)的执行者,您可以使用 dispatch
(或 post
):
auto s1_handler = [&](auto chain) {
assert(s1.running_in_this_thread());
dispatch(get_associated_executor(chain, s1), chain);
};
事实上,如果您确定 chain
将有一个关联的执行程序,你可以接受默认的回退(这是一个系统执行程序):
auto s1_handler = [&](auto chain) {
assert(s1.running_in_this_thread());
dispatch(chain);
};
在简化的扩展测试器中展示智慧:
#include <boost/asio.hpp>
#include <functional>
#include <iostream>
namespace asio = boost::asio;
int main() {
asio::thread_pool io(1);
auto s1 = make_strand(io), s2 = make_strand(io);
assert(s1 != s2); // implementation defined! strands may hash equal
auto s1_handler = [&](auto chain) {
assert(s1.running_in_this_thread());
// immediate invocation runs on the current strand:
chain();
// dispatch *might* invoke directly if already on the right strand
dispatch(chain); // 1
dispatch(get_associated_executor(chain, s1), chain); // 2
// posting never immediately invokes, even if already on the right
// strand
post(chain); // 3
post(get_associated_executor(chain, s1), chain); // 4
};
int count_chain_invocations = 0;
auto s2_handler = [&] {
if (s2.running_in_this_thread()) {
count_chain_invocations += 1;
} else {
std::cout << "(note: direct C++ call ends up on wrong strand)\n";
}
};
{
using erased_handler = std::function<void()>;
erased_handler handle = bind_executor(s2, s2_handler);
assert(asio::get_associated_executor(handle, s1) == s1);
}
{
auto handle = bind_executor(s2, s2_handler);
assert(asio::get_associated_executor(handle, s1) == s2);
}
auto handle = bind_executor(s2, s2_handler);
post(s1, std::bind(s1_handler, handle));
io.join();
std::cout << "count_chain_invocations: " << count_chain_invocations << "\n";
}
所有断言均通过,输出符合预期:
(note: direct C++ call ends up on wrong strand)
count_chain_invocations: 4
无论你做什么,都不要使用 std::function
.不过,您可以包装一个;
template <typename Sig> struct ErasedHandler {
using executor_type = asio::any_io_executor;
std::function<Sig> _erased;
executor_type _ex;
executor_type get_executor() const { return _ex; }
template <typename F>
explicit ErasedHandler(F&& f)
: _erased(std::forward<F>(f))
, _ex(asio::get_associated_executor(f)) {}
ErasedHandler() = default;
template <typename... Args>
decltype(auto) operator()(Args&&... args) const {
return _erased(std::forward<Args>(args)...);
}
template <typename... Args>
decltype(auto) operator()(Args&&... args) {
return _erased(std::forward<Args>(args)...);
}
explicit operator bool() const { return _erased; }
};
在你这样做之前,请注意
any_io_executor
还键入删除执行程序,这可能会损害性能我可能会避免一般地存储类型删除的可链接处理程序。您通常可以存储由模板类型参数推导的处理程序的实际类型。
您可能期望的是这种行为:
template <typename... Args>
decltype(auto) operator()(Args&&... args) const {
// CAUTION: NOT WHAT YOU WANT
boost::asio::dispatch(_ex,
std::bind(_erased, std::forward<Args>(args)...));
}
template <typename... Args>
decltype(auto) operator()(Args&&... args) {
// CAUTION: NOT WHAT YOU WANT
boost::asio::dispatch(_ex,
std::bind(_erased, std::forward<Args>(args)...));
}
看到那个 Live On Coliru
在这种情况下,即使是直接的 C++ 调用也会“做正确的事”。
这看起来不错。直到你想到为止。
问题是处理程序不能以这种方式反弹。更具体地说,如果您有一个与“自由线程”执行程序关联的处理程序,则执行 bind_executor(strand, f)
不会有任何影响(除了减慢你的程序),因为 f
无论如何都会令人讨厌地 dispatch 给另一个执行者。
所以不要那样做:)
关于c++11 - boost::asio::bind_executor 不在 strand 中执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71475533/
strand::wrap() 的行为被定义为它创建一个仿函数,该仿函数将在调用时执行 strand::dispatch()。我最近在我们的一个执行以下序列的应用程序中遇到了一个错误: my_great
strand::post() 和 strand::wrap() 之间的性能差异是什么?使用 strand::wrap() 时关于竞争条件的故事是什么? 最佳答案 只是为了澄清,strand::wrap
如果有一个通用方法接受一些处理程序: template void Register( HandlerType && handler ) { m_handler( std::forward( h
由于最新版本的 boost,asio 推出了新的执行器并提供了 asio::strand .所以现在完全可以使用 asio::strand而不是 io_context::strand .但它们不能互换
我有一份 asio::io_service::strand . 复制的链及其源是不同的执行者吗?换句话说,传递给复制链的函数和传递给源链的另一个函数是否可能由两个不同的线程同时执行? 或者这两个分支在
我必须开发一个与服务器对话的异步客户端。客户端在与主应用程序不同的线程中运行,并使用回调链读取服务器发送的内容。每个读取处理程序通过一个链注册下一个(它有点复杂,因为我使用类方法作为回调,所以我需要绑
我对使用 strand::wrap 和 strand::post 发帖之间的区别感到迷惑不解?似乎两者都保证序列化,但是如何使用 wrap 序列化而不获得一致的顺序?似乎他们都必须做同样的事情。我什么
在this blog post (2010) ,有人试图使用 Boost::strand 工具解决生产者/消费者问题。我感觉他没有捕获要点,他的程序从来没有同时运行一些生产者和一些消费者,但我不是 b
我有一个关于什么在 strand 中运行,什么不运行的问题。我已经阅读了关于 SO 的帖子以及 strand 的文档,但我想确保我已经正确理解了它与下面代码的关系。 下面的类是一个 tcp 客户端,它
我正在尝试将一些事件处理程序存储在数组中。其中一些被 boost::strand::wrap 包裹。 boost::function proc_handlers[] = { boost::bi
我编写此服务器代码已有一段时间了。我从超过 1 个线程调用 io_service::run,但我不确定我是否需要在这里使用 strand。因为我从不多次调用 async_read 。如果其他连接需要向
这是我的代码: void client_connection::serve() { asio::async_read(this->socket_, asio::buffer(&buffer_,
我正在编写一个程序,将读取的数据转发给另一个对等点。我有一个在每次读取套接字上的数据后调用的方法。此方法将数据发布到一个链中以将其写回另一个对等点。当发送大块数据时,应用程序发回的数据与接收到的数据不
我最近使用了 boost::asio::strand。我对此没有经验。 如果我用 strand::wrap() 调用 function1 并用 调用 function2 ,我遇到了什么~functio
我想在我的代码中添加一个运行时断言,以检查代码是否受到给定链的保护。 这是一个伪例子: ... asio::io_service my_io_service; asio::strand my_stra
看了boost::asio的文档,还是不清楚什么时候需要用asio::strand。假设我有一个使用 io_service 的线程,那么按如下方式在套接字上写入是否安全? void Connectio
boost::asio 库提供了一个有趣的同步模型,使用“strands”来序列化对通常需要锁的资源的访问。这实际上通过将每个锁定操作转变为入队来 boost 并行性。 搜索“strands”只会产生
asio::async_write(m_socket, asio::buffer(buf, bytes), custom_alloc(m_strand.wrap(cus
我使用的是最新的 ASIO 版本(目前为 1.18.0)。目前正在设计一个带有定时器(用于超时)的多线程异步 TCP 服务器。我有一个 io_context 有多个线程调用它的 run() 函数。我接
在 Java 中我们有“线程”,在 CPython 中我们有线程(非并发)和“进程”。 在 JS 中,当我启动一个 async 函数或方法时,我如何正式引用这些“执行代码链”? 我听说每个这样的代码块
我是一名优秀的程序员,十分优秀!