c++ - 我需要在此服务器模型中使用 boost::strand 吗?

标签 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()));
}

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

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's async_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/

相关文章:

c++ - boost HTTP 服务器问题

c++ - 如何使用 boost::asio 在 http 上进行 POST?

c++ - Boost.Asio 与谷歌 Protocol Buffer

c++ - 停止后 boost asio 套接字无法连接

c++ - 获取低于秒的值

c++ - C/C++ 矩阵乘法顺序

c++ - 为什么我不能使用双冒号在命名空间中前向声明一个类?

c++ - 遍历通用节点类,列出所有节点导致无限循环

C++ 无效反转函数

c++ - 将 unique_ptr 引用传递给 boost::bind?