我编写此服务器代码已有一段时间了。我从超过 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()));
}
我不确定这是否是线程安全的。如果只有发送部分不是线程安全的,因为我可以一次发送多个数据包(这将会发生),我应该只在那里使用一个链吗?
Z
最佳答案
是的,你需要一个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(...); |
此外,Session::SendPacket()
中的send_mut
互斥量没有为m_socket
提供真正的安全。 boost::asio::async_write()
operation 是一个组合操作,由对 m_socket.async_write_some()
函数的零次或多次调用组成。由于锁的生命周期和异步操作的行为,锁不保证在任何或所有 async_write_some()
操作期间保持互斥量。
有关线程安全和链的更多详细信息,请考虑阅读 this回答。
此外,请注意,一旦在流上启动了 async_write()
操作,程序必须确保在调用初始操作的完成处理程序之前不会在流上发生其他写入:
The program must ensure that the stream performs no other write operations (such as
async_write
, the stream'sasync_write_some
function, or any other composed operations that perform writes) until this operation completes.
如果需要执行多次写入,则考虑对写入进行排队,如this所示。回答。这种类型的解决方案还可以更轻松地管理 packet
对象的底层缓冲区的生命周期,因为队列将拥有数据拷贝,满足 async_write()
操作的要求缓冲区的底层内存块在调用完成处理程序之前保持有效。
关于c++ - 我需要在此服务器模型中使用 boost::strand 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24364372/