通过多线程的共享缓冲区提升asio async_write

时间:2014-03-31 07:27:33

标签: c++ boost boost-asio

现在我有一个Connection类,如下所示(省略了相关的内容):

class Connection : public std::enable_shared_from_this<Connection> {
public:
    virtual void write() {
        socket_->async_write_some(boost::asio::buffer(buffer_.data(),
                                                      buffer_.size()),
                                  std::bind(&Connection::on_written,
                                            shared_from_this(),
                                            std::placeholders::_1,
                                            std::placeholders::_2));
    }

    void on_written(const boost::system::error_code& e, std::size_t length) {
        if(e) {
            // handle error here
            return;
        }

        buffer_.consume(length);
    }

    void add_to_buf(const std::string& data) {
        // add the string data to buffer_ here
    }

private:
    boost::asio::io_service& service_;
    std::unique_ptr<socket> socket_;

    boost::asio::streambuf buffer_;
};

如您所见,write()操作将在buffer_中写入数据,buffer_仅在写操作完成处理程序中清除。但是,问题来了,现在我有以下调用代码(注意:它是多线程的):

Connection conn;
// initialization code here
conn.add_to_buf("first ");
conn.write();
conn.add_to_buf("second");
conn.write();

我想要的输出是first second,但有时输出可能是first first second。它发生在第二个操作开始但未调用第一个完成处理程序时。我已经阅读了strand来序列化事物,但是,它只能序列化任务,它不能序列化完成处理程序和任务。

有人可能会建议在第一个完成处理程序中调用第二个写操作,但是,根据设计,这是无法实现的。

那么,有什么建议吗?可能锁定buffer_

1 个答案:

答案 0 :(得分:2)

锁定缓冲区本身不会改变任何东西。如果在第一次写入完成之前调用write,它将再次发送相同的数据。在我看来,最好的方法是放弃add_to_buf方法,只需坚持一个write函数同时执行这两个操作,将数据添加到缓冲区,如果必要则触发发送。

class Connection : public std::enable_shared_from_this<Connection> {
public:
    virtual void write(const std::string& data) {
        std::lock_guard<std::mutex> l(lock_);
        bool triggerSend = buffer_.size() == 0;
        // add data to buffer
        if (triggerSend) {
            do_send_chunk();
        }
    }

    void on_written(const boost::system::error_code& e, std::size_t length) {
        if (e) {
            // handle error here
            return;
        }

        std::lock_guard<std::mutex> l(lock_);
        buffer_.consume(length);
        if (buffer_.size() > 0) {
            do_send_chunk();
        }
    }

private:
    void do_send_chunk() {
        socket_->async_write_some(boost::asio::buffer(buffer_.data(),
            buffer_.size()),
            std::bind(&Connection::on_written,
            shared_from_this(),
            std::placeholders::_1,
            std::placeholders::_2));
    }

    boost::asio::io_service& service_;
    std::unique_ptr<socket> socket_;

    boost::asio::streambuf buffer_;
    std::mutex lock_;
};

这个想法是,写入函数检查缓冲区中是否还有数据。在这种情况下,它不必触发do_send_chunk调用,因为新的数据将保留在缓冲区和on_written中,然后将调用另一个do_send_chunkif(buffer_.size() > 0)on_written内{1}}为真。但是,如果没有数据,则必须触发发送操作。