C++ io_服务::轮询一个非确定性行为

C++ io_服务::轮询一个非确定性行为,c++,boost,boost-asio,C++,Boost,Boost Asio,在下面的代码中,我希望输出总是1,因为我希望在调用poll\u one()时只运行一个处理程序。然而,大约300次中有一次,输出实际上是3。根据我对boost库的理解,这似乎是不正确的。非确定性行为是错误还是预期 #include <boost/asio.hpp> int main() { boost::asio::io_service io; boost::asio::io_service::work io_work(io); boost::asio::io_serv

在下面的代码中,我希望输出总是1,因为我希望在调用
poll\u one()
时只运行一个处理程序。然而,大约300次中有一次,输出实际上是3。根据我对boost库的理解,这似乎是不正确的。非确定性行为是错误还是预期

#include <boost/asio.hpp>

int main() {
  boost::asio::io_service io;
  boost::asio::io_service::work io_work(io);
  boost::asio::io_service::strand strand1(io);
  boost::asio::io_service::strand strand2(io);
  int val = 0;

  strand1.post([&val, &strand2]() {
    val = 1;
    strand2.post([&val]() {
      val = 2;
    });
    boost::asio::spawn(strand2, [&val](boost::asio::yield_context yield) {
      val = 3;
    });
  });

  io.poll_one();
  std::cout << "Last executed: " << val << std::endl;

  return 0;
}
#包括
int main(){
boost::asio::io_服务io;
boost::asio::io_服务::工作io_工作(io);
boost::asio::io_服务::绞线1(io);
boost::asio::io_服务::绞线2(io);
int-val=0;
strand1.post([&val,&strand2](){
val=1;
strand2.post([&val](){
val=2;
});
boost::asio::spawn(strand2,[&val](boost::asio::yield\u上下文yield){
val=3;
});
});
io.poll_one();

std::cout观察到的行为定义明确,预期会发生,但人们不应期望它经常发生

Asio的串实现池有限,串的默认分配策略为散列。如果发生散列冲突,两个串将使用相同的实现。当发生散列冲突时,示例简化为以下内容:

#包括
#包括
int main()
{
boost::asio::io_服务io_服务;
boost::asio::io_服务::strand strand1(io_服务);
//让strand2使用与strand1相同的实现。
boost::asio::io_服务::绞线2(绞线1);
int值=0;
自动句柄1=[&value,&strand1,&strand2](){
断言(strand1.running_in_this_thread());
断言(strand2.running_in_this_thread());
数值=1;
//handler2排队进入串中,从未被调用。
自动句柄2=[&value](){assert(false);};
2.邮递员(2);;
//handler3立即执行。
自动句柄3=[&value](){value=3;};
2.调度(装卸工3);
断言(值==3);
};
//排队等候处理者1。
1.立柱(扶手1);
//运行事件处理循环,执行handler1。
断言(io_service.poll_one()==1);
}
在上述示例中:

  • io\u服务。poll\u one()
    执行单个就绪处理程序(
    handler1
  • handler2
    永远不会被调用
  • handler3
    在中立即调用,因为
    strand2.dispatch()
    是从处理程序中调用的,其中
    strand2.running\u this\u thread()
    返回
    true

观察到的行为有多种细节:

  • 将运行
    io_服务的事件循环,并且在没有阻塞的情况下,它将最多执行一个准备运行的处理程序。在
    dispatch()
    上下文中立即执行的处理程序从不排队进入
    io_服务
    ,并且不受
    poll_one()
    调用单个处理程序的限制

  • 重载会启动一个堆栈式的协同路由,就像通过
    strand.dispatch()

    • 如果
      strand.running\u in\u this\u thread()
      为调用者返回
      false
      ,则协同路由将被发布到
      strand
      中以进行延迟调用
    • 如果
      strand.running\u in\u this\u thread()
      为调用者返回
      true
      ,则将立即执行协同路由
  • 使用相同实现的离散
    对象仍保持串的保证。即,不会发生并发执行,并且定义良好。当离散
    对象使用离散实现,且多个线程运行
    io_服务
    时,可能会发现观察并发执行的离散链。但是,当离散
    对象使用相同的实现时,即使多个线程正在运行
    io_服务
    ,也不会观察到并发。此行为是:

    该实现不保证通过不同的串对象发布或调度的处理程序将被并发调用

  • Asio的串实现池有限。当前默认值为
    193
    ,可以通过将
    BOOST\u Asio\u串实现
    定义为所需数量来控制。此功能在

    通过将
    BOOST\u ASIO\u strand\u实现
    定义为所需的数量,使strand实现的数量可配置

    通过减小池大小,可以增加两个离散链使用相同实现的可能性。对于原始代码,如果将池大小设置为
    1
    ,则
    strand1
    strand2
    将始终使用相同的实现,导致
    val
    始终为
    3
    ()

  • 分配串实现的默认策略是使用黄金比例散列。使用散列算法时,可能会发生冲突,导致同一个实现用于多个离散的
    对象。通过定义
    BOOST\u ASIO\u ENABLE\u SEQUENTIAL\u串分配
    ,可以将分配策略更改为循环,以防止冲突发生,直到出现
    BOOST\u ASIO\u STRAND\u实现+1
    STRAND分配。此功能在BOOST中有所说明。ASIO 1.48发行说明:

    增加了对新的
    BOOST\u ASIO\u ENABLE\u SEQUENTIAL\u STRAND\u分配
    标志的支持,该标志将串实现的分配切换为使用循环方法,而不是散列

鉴于上述详细信息,当在原始代码中观察到
1
时,会发生以下情况:

  • strand1
    strand2
    具有独立的实现
  • io\u服务::poll\u one()
    执行直接发布到
    strand1
  • 在中发布的处理程序