我是否需要在此服务器模型中使用boost :: strand?

时间:2014-06-23 11:08:06

标签: c++ boost-asio

我一直在编写这个服务器很长一段时间了。我从超过1个线程调用io_service :: run,但我不确定我是否需要在这里使用strand。因为我从来没有多次调用async_read。虽然如果其他连接需要向其他人发送内容,我可能会多次调用async_write。这是我到目前为止的代码

void Session::RecvPacket()
{
    boost::asio::async_read(m_socket, boost::asio::buffer(m_recv_buffer, len),
        boost::bind(&Session::OnRead, this, boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));

}

void Session::OnRead(const boost::system::error_code &e, size_t packet_length, size_t bytes_transferred)
{
    if (e || bytes_transferred != packet_length)
    {
        if (e == boost::asio::error::operation_aborted)
            return;

        Stop();
        return;
    }

    // do something and most likely send a packet back

    RecvPacket();
}
void Session::SendPacket(packet &p)
{
    boost::mutex::scoped_lock lock(send_mut);

    dword len = p.Lenght(); 

    m_crypt->makeheader(p.GetRaw().get(), static_cast<word>(len - 4));
    m_crypt->encrypt(p.GetRaw().get() + 4, len - 4);

    boost::asio::async_write(m_socket, boost::asio::buffer(p.GetRaw().get(), len),
        boost::bind(&Session::OnPacketSend, this, len, boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred, p.GetRaw()));
}

我不确定这是否是线程安全的。如果只发送部分不是线程安全的,因为我可以一次发送多个数据包(这将会发生)我应该只使用一个链吗?

Gz

1 个答案:

答案 0 :(得分:1)

是的,您需要strand

tcp::socket文档中所述,同一套接字(即共享对象)上的并发调用不是线程安全的。但是,在对象上挂起多个异步操作是安全的。因此,这是安全的:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive(...);            |
socket.async_write_some(...);         |

这是安全的:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive(...);            |
                                      | socket.async_write_some(...);

但是这被指定为不安全:

 thread_1                             | thread_2
--------------------------------------+---------------------------------------
socket.async_receive(...);            | socket.async_write_some(...);
                                      |

此外,send_mut中的Session::SendPacket()互斥锁并未对m_socket提供真正的安全保障。 boost::asio::async_write()操作是组合操作,由对m_socket.async_write_some()函数的零次或多次调用组成。由于锁的生命周期和异步操作的行为,锁不保证在任何或所有async_write_some()操作期间都将保持互斥锁。

有关线程安全和线索的更多详细信息,请考虑阅读this answer。


另外,请注意,一旦在流上启动async_write()操作,程序必须确保在调用初始操作的完成处理程序之前,流上不会发生其他写操作:

  

程序必须确保流不执行任何其他写操作(例如async_write,流的async_write_some函数或执行写入的任何其他组合操作),直到此操作完成。

如果需要执行多次写入,请考虑排队写入,如this answer所示。这种类型的解决方案还可以更轻松地管理packet对象的底层缓冲区的生命周期,因为队列将拥有数据的副本,满足async_write()操作要求缓冲区的底层内存块保持有效直到调用完成处理程序。