我需要在这个服务器模型中使用 boost::strand 吗?
Posted
技术标签:
【中文标题】我需要在这个服务器模型中使用 boost::strand 吗?【英文标题】:Do I need to use a boost::strand in this server model? 【发布时间】:2014-06-23 11:08:06 【问题描述】:我已经为这个服务器编写代码已经有一段时间了。我从超过 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()));
我不确定这是否是线程安全的。如果只有发送部分不是线程安全的,因为我可以一次发送超过 1 个数据包(这将会发生),我应该只使用一个链吗?
Gz
【问题讨论】:
【参考方案1】:是的,您需要strand
。
如tcp::socket
文档中所述,同一套接字(即共享对象)上的并发调用不是线程安全的。但是,在一个对象上挂起多个异步操作是安全的。因此,这是安全的:
thread_1 |线程_2 --------------------------------------------------+------------ ---------------------------- socket.async_receive(...); | socket.async_write_some(...); |
这是安全的:
thread_1 |线程_2 --------------------------------------------------+------------ ---------------------------- socket.async_receive(...); | | socket.async_write_some(...);
但这被指定为不安全:
thread_1 |线程_2 --------------------------------------------------+------------ ---------------------------- socket.async_receive(...); | socket.async_write_some(...); |
此外,Session::SendPacket()
中的 send_mut
互斥锁不会为 m_socket
提供真正的安全性。 boost::asio::async_write()
操作是一个组合操作,由对m_socket.async_write_some()
函数的零个或多个调用组成。由于锁的生命周期和异步操作的行为,锁不保证在任何或所有async_write_some()
操作期间都会持有互斥锁。
有关线程安全和链的更多详细信息,请考虑阅读this 答案。
另外,请注意,一旦在流上启动 async_write()
操作,程序必须确保在调用初始操作的完成处理程序之前不会在流上发生其他写入:
程序必须确保流不执行其他写入操作(例如
async_write
、流的async_write_some
函数或执行写入的任何其他组合操作),直到此操作完成。
如果需要执行多个写入,则考虑将写入排队,如this答案所示。这种类型的解决方案还可以更轻松地管理 packet
对象的底层缓冲区的生命周期,因为队列将拥有数据的副本,满足 async_write()
操作的要求,即缓冲区的底层内存块在完成处理程序之前保持有效被调用。
【讨论】:
以上是关于我需要在这个服务器模型中使用 boost::strand 吗?的主要内容,如果未能解决你的问题,请参考以下文章