【问题标题】:asio strand and data synchronizationasio链和数据同步
【发布时间】:2013-02-08 23:24:30
【问题描述】:

我发现 asio 关于strand 同步的文档是晦涩难懂的。它只是说在线程池设计 asio 应用程序中,处理程序可以在任何调用 io_service::run() 的线程中运行。使用strand 扭曲这些处理程序可以使它们的执行正确并发。在其example3中,所有handle_read都被strand包裹,我认为Connection类中的变量如buffer已经被strand同步,不同的线程调用handle_read会起来-最新数据,没关系。但是在Connection 类中定义的一个数据成员也被处理程序访问但未被strand 包装呢?我认为这是一个问题,不是吗?

在其文档example3 中,为什么handle_accept 没有被strand 包裹? new_connection_ 由多线程访问:new_connection_.reset 由线程 A 调用,server::handle_accept 由线程 B 调用。我认为它需要在这里进行数据同步,否则线程 B 可能会使用其过期的 new_connection_尚未调用 reset。

【问题讨论】:

    标签: boost-asio


    【解决方案1】:

    HTTP Server 3 的设计方式使其实际上不需要strand

    Boost.Asio 的一个基本特征是,对于给定的操作,处理程序最多会被调用一次。这种行为允许将异步编程的调用路径更多地设想为调用链。

    例如,检查server 接受连接的图示调用链:

    server::server(...)
    {
      start_accept();  --.
    }                    |
        .----------------'
        |      .----------------------------------------.
        V      V                                        |
    void server::start_accept()                         |
    {                                                   |
      new_connection_.reset(new connection(...));       |
      acceptor_.async_accept(..., handle_accept);  --.  |
    }                                                |  |
        .--------------------------------------------'  |
        |                                               |
        V                                               |
    void server::handle_accept(...)                     |
    {                                                   |
      if (!error)                                       |
      {                                                 |
        new_connection_->start();                       |
      }                                                 |    
      start_accept();  ---------------------------------'
    }
    

    如图所示,仅存在一个异步事件链。在new_connection 上不可能同时执行处理程序或操作,据说它在隐式链中运行。处理程序server::handle_accept 运行的线程无关紧要。

    this 问题中回答了 connection::handle_read 调用链和有关 strand 的更多详细信息。

    【讨论】:

    • 我得到 2 点:1,在 asio 中,处理程序调用链是并发的,因为文档说“隐式链”。 2、HTTP 示例 3 的 handle_read 不需要包裹在 strand 中,因为它构成了一个链。我说的对吗?
    • @jean:在图示的链中,有一个与acceptor_ 关联的异步操作链。因此,与该链关联的处理程序不可能同时执行。另一方面,这些处理程序可能与另一个链的处理程序同时运行,例如每个connectionhandle_read。是的,handle_read 中的 strand 不是必需的,因为链形成了隐式链。
    【解决方案2】:

    我认为您在某种程度上错过了 strand 的含义。 它不同步数据访问。它同步处理程序调用。这可以理解为“所有被给定的strand包裹的处理程序都不会被同时调用”。

    所以,您的第一个问题是:如果某些处理程序没有被 strand 包裹 - 现在可以同时调用它。因此它会受到同步问题和/或 RC 的影响。请注意,如果您包裹在一个地方并不意味着您受到 RC 的保护;这应该在每次通话中完成。因为 strand 不知道你从其他线程调用了什么,除非你使用 .wrap

    第二个问题:在给定示例 start_accept 中设置接受处理程序 handle_accept,而 handle_accept 正在设置新的接受处理程序(通过调用 start_accept)。因此,它们不会被同时调用,因为您不能创建 2 个或更多 async_accept 事件。当然,如果其他线程为同一个“服务器”实例调用 start_accept - 此示例可能/将会失败,但这样做显然是错误的。

    【讨论】:

    • 我的经验,线程同步意味着数据和代码执行。缺少任何一个,同步将毫无意义。因为锁可以同时实现。比如strand不做数据同步,buffer数据成员就会有问题。 start_accept() & handle_accept() 会在不同的线程中执行,为什么和数据成员'new_connection_'的数据同步没有关系呢?
    • start_accept() & handle_accept() 不能同时执行。当start_accept 运行时,没有活动的async_accept,所以不能调用handle_accept。另一方面,当调用handle_accept 时,io_service 中不再有这样的处理程序;当您调用async_accept() 时,它将被添加到handle_accept 的末尾。但是,有一个错误。由于new_connection_->start()没有保存new_connection_shared_ptr,所以会在async_accept中重写。所以你是对的,这可能会失败,但在其他地方(在连接类中)。请注意,这是不完整的示例
    • 我知道它们不会由不同的线程同时执行。我的问题是当线程 B 完成 start_accept() 执行后线程 A 调用 handle_accept() 时,如何同步共享变量 'new_connection_' 状态。因为线程 B 调用 'new_connection_' reset() 会改变它的状态。如果没有任何显式同步,线程 A 如何看到这些状态变化?
    • 由于start_accept() 被设计为只从对象外部启动一次,而从对象内部多次启动,所以new_connection_.reset() 只有在handle_accept 准备好时才会调用;并在同一个线程中。如果在这个实例已经运行的时候某个线程调用了start_accept(),那将会很麻烦。但这样做是用户的错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多