现在我有一个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_
?
答案 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_chunk
。if(buffer_.size() > 0)
在on_written
内{1}}为真。但是,如果没有数据,则必须触发发送操作。