Boost.Iostreams使用问题:无法写入文件及即时写入需求
解决Boost IOStreams输出流即时写入文件的问题
问题背景
开发日志函数时,需要根据用户配置选择写入.gz压缩文件或普通文件。原代码使用指针创建流对象,当关闭压缩(_compressed = false)时,文件能正常创建但内容无法即时写入;编写最小示例后发现,所有内容只有在程序退出时才会被写入文件。需要实现每次<<输出操作时就将内容写入文件,但因日志操作频繁,不想通过关闭文件触发刷新(担心开销过高)。
原代码片段:
typedef boost::iostreams::filtering_streambuf<boost::iostreams::output> boost_output_buffer; . . . fstream *tmp_file; if(_compressed){ tmp_file = new ofstream(file_path, ios_base::out | ios_base::binary | ios_base::app); } else { tmp_file = new ofstream(file_path, ios_base::out | ios_base::app); } boost_output_buffer *outbuf = new boost_output_buffer(); if(_compressed){ outbuf->push(*(new boost::iostreams::gzip_compressor())); } outbuf->push((*tmp_file)); _fileStreamPtr = new ostream(outbuf); . . . (*_fileStreamPtr) << "Hello World\n";
最小示例代码:
#include <fstream> #include <iostream> #include <string> #include <boost/iostreams/filtering_streambuf.hpp> #include <boost/iostreams/filter/gzip.hpp> #include "unistd.h" using namespace std; typedef boost::iostreams::filtering_streambuf<boost::iostreams::output> boost_output_buffer; int main(int argc, char* argv[]){ ofstream tmp_file(argv[1], ios_base::out | ios_base::app); boost_output_buffer outbuf; outbuf.push(tmp_file); ostream mystream(&outbuf); (mystream) << "Hello World\n"; sleep(15); return 0; }
问题原因
C++标准输出流和Boost IOStreams默认都会使用缓冲区优化性能:数据先暂存在内存缓冲区中,只有当缓冲区填满、流被关闭,或者手动触发刷新时,才会将内容写入底层文件。你的代码中没有主动触发刷新操作,所以内容一直留在缓冲区,直到程序退出时才被自动处理。
解决方案
有两种方式可以实现即时写入,同时避免频繁关闭文件的开销:
1. 手动调用flush()方法
每次写入日志后,调用流的flush()方法,强制将缓冲区内容写入文件:
#include <fstream> #include <iostream> #include <string> #include <boost/iostreams/filtering_streambuf.hpp> #include <boost/iostreams/filter/gzip.hpp> #include <unistd.h> using namespace std; typedef boost::iostreams::filtering_streambuf<boost::iostreams::output> boost_output_buffer; int main(int argc, char* argv[]){ ofstream tmp_file(argv[1], ios_base::out | ios_base::app); boost_output_buffer outbuf; outbuf.push(tmp_file); ostream mystream(&outbuf); mystream << "Hello World\n"; mystream.flush(); // 手动刷新缓冲区,内容立即写入文件 sleep(15); return 0; }
2. 启用unitbuf自动刷新标志
给输出流设置unitbuf标志,让每次输出操作完成后自动刷新缓冲区,无需手动调用flush():
#include <fstream> #include <iostream> #include <string> #include <boost/iostreams/filtering_streambuf.hpp> #include <boost/iostreams/filter/gzip.hpp> #include <unistd.h> using namespace std; typedef boost::iostreams::filtering_streambuf<boost::iostreams::output> boost_output_buffer; int main(int argc, char* argv[]){ ofstream tmp_file(argv[1], ios_base::out | ios_base::app); boost_output_buffer outbuf; outbuf.push(tmp_file); ostream mystream(&outbuf); mystream << unitbuf; // 启用自动刷新,每次<<操作后都会写入文件 mystream << "Hello World\n"; // 内容即时写入 sleep(15); return 0; }
原代码的额外优化
原代码使用裸指针创建对象存在内存泄漏风险,建议改用栈对象或智能指针,并简化压缩逻辑:
typedef boost::iostreams::filtering_streambuf<boost::iostreams::output> boost_output_buffer; // 用栈对象替代裸指针,避免内存泄漏 ios_base::openmode mode = ios_base::out | ios_base::app; if (_compressed) { mode |= ios_base::binary; } ofstream tmp_file(file_path, mode); boost_output_buffer outbuf; if (_compressed) { outbuf.push(boost::iostreams::gzip_compressor()); // 直接使用栈对象,无需手动释放 } outbuf.push(tmp_file); ostream fileStream(&outbuf); fileStream << unitbuf; // 启用自动刷新 // 写入日志 fileStream << "Hello World\n";
注意事项
- 启用
unitbuf会带来轻微的性能开销(每次输出都触发IO操作),但对于日志场景通常是可接受的;如果追求性能,可以在批量写入后调用一次flush(),或者只在关键日志后手动刷新。 - 压缩场景下,gzip压缩器本身也有内部缓冲,即使刷新外层流,压缩器可能仍会暂存部分数据;如果需要绝对即时的压缩写入,可以调整gzip压缩器的参数,减少内部缓冲(比如设置
flush_level为boost::iostreams::flush)。
内容的提问来源于stack exchange,提问作者Aditya Agarwal
相关产品推荐
相关产品推荐

