【问题标题】:Is ip::tcp::socket.close() thread safe?ip::tcp::socket.close() 线程安全吗?
【发布时间】:2014-02-01 17:07:05
【问题描述】:

如果套接字上存在async_read 正在进行,则应该有io_service 的内部线程检查套接字的状态。从另一个线程调用socket.close() 是否安全(也许当它运行io_service 的单独处理程序时)?

我的意思是即使我可以保证我的处理程序不会同时使用 asio socket,它是否足够好(考虑到 io_service 的内部线程时)?

更新: 我在堆栈协程中使用async_read。与this doc 底部的示例非常相似,只是我的函数调用层多了一层yield_context。如果我在该示例中在my_strand 中调度socket.close() 操作,那么整个线程安全吗?换句话说,所有相关操作(async_read、中间async_read_some、堆栈式协程的隐式处理程序、socket.close())是否会通过单链运行my_strand

【问题讨论】:

    标签: c++11 boost-asio


    【解决方案1】:

    一般来说,并发调用同一个套接字对象是不安全的1async_read() 组合操作由零个或多个中间 async_read_some() 操作组成。这些中间操作仅在当前调用io_service::run() 的线程中启动。内部线程相当透明,Platform-Specific Implementation Notes 中列出的线程都不会出现问题。

    因此:

    • 如果单个线程正在调用io_service::run() 并且socket.close() 是从处理程序中调用的,那么它是安全的,因为不可能并发执行。文档将此称为隐式链。
    • 如果单个线程正在调用io_service::run() 并且从处理程序外部调用socket.close(),则它是不安全的,因为socket 可能有两个并发调用:close()io_service 外部调用和async_read_some() 来自当前正在调用 io_service::run() 的线程。为了使其线程安全,请将处理程序发布到调用socket.close()io_service
    • 如果多个线程正在调用io_service::run(),则需要显式strand 以保证线程安全。 async_read() 需要从 strand 内部启动,并且它的完成处理程序也必须是同一链的 wrapped。此外,socket.close() 应该是 dispatched 通过链。

    对于堆栈式协程,使用接受strandspawn() 重载将在strand 的上下文中执行提供的函数。此外,当yield_context 对象作为处理程序传递给异步操作时,处理程序,包括来自组合操作的中间处理程序,在strand 的上下文中被调用。因此,为了确保线程安全,socket.close() 必须是:

    • 在协程中调用:

      // The lambda will execute within the context of my_strand.
      boost::asio::spawn(my_strand,
        [socket&](boost::asio::yield_context yield)
        {
          // In my_strand.
          // ...
      
          // The socket.async_read_some() operations that composed async_read()
          // will run within the context of my_strand.
          async_read(socket, ..., yield);
      
          // Still within my_strand.
          socket.close();
        });
      
    • 明确发送到my_strand:

      // The lambda will execute within the context of my_strand.
      boost::asio::spawn(my_strand,
        [socket&](boost::asio::yield_context yield)
        {
          // In my_strand.
          // ...
      
          // The socket_.async_read_some() operations that composed async_read()
          // will run within the context of my_strand.
          async_read(socket, ..., yield);
        });
      
      my_strand.dispatch([socket&](){ socket.close(); });
      

    有关线程安全、组合操作和链的更多详细信息,请考虑阅读this 答案。


    1。 revision history 记录了此规则的异常情况。如果操作系统支持,同步读取、写入、接受和连接操作是线程安全的。为了完整起见,我将其包含在此处,但建议谨慎使用。

    【讨论】:

    • 哇!很专业的回答!我的情况涉及堆栈协程。我在我的问题中添加了那部分。请看一看。
    • @updogliu 很高兴答案很有帮助。我已经更新了它以涵盖堆栈协程。
    • 非常感谢!这个答案值得纳入 ASIO 文档!
    猜你喜欢
    • 2020-04-15
    • 2011-07-04
    • 2014-04-26
    • 2012-11-30
    • 2010-12-30
    • 2013-03-12
    • 2021-08-03
    • 2010-12-27
    • 2018-06-04
    相关资源
    最近更新 更多