【问题标题】:Opening file only once before writing to it in an async loop using boost::asio在使用 boost::asio 在异步循环中写入文件之前只打开一次文件
【发布时间】:2022-10-13 05:33:08
【问题描述】:

基于我之前发布的一个问题,我实现了一个单独的异步线程,它使用 boost::asio 库中的 stable_timer 每 10 秒转储一次容器的内容。它看起来如下:

m_outfile.open("numbers.bin", std::ios::out | std::ios::trunc | std::ios::binary);
            
for (auto val : number_container) {
                m_outfile.write(reinterpret_cast<const char*>(&val), sizeof(int));

                if (m_outfile.bad()) {
                    throw std::runtime_error("Error in writing to numbers.bin");
                }
            }

            m_timer.expires_at(m_timer.expiry() + boost::asio::chrono::seconds(NUM_SECONDS_DUMP));
            m_timer.async_wait(boost::bind(&Data_dump::dump, this));

这只是代码的一部分,但您可以看到我打开了一个文件,循环通过一个容器并将内容写入文件。 这里的问题在于第一行 - 它只能使用一次,否则当我再次输入此代码时,代码会在 10 秒后崩溃。我在第一行周围放了一个 while 循环,它只运行一次,如下所示:

            while (testing == 0) {
                m_outfile.open("numbers.bin", std::ios::out | std::ios::trunc | std::ios::binary);
                testing++;
            }

这很好用,因为测试是一个全局变量,但我不确定是否要声明一个全局变量并且必须一直检查这个 while 循环,即使它永远不会进入它。只是为这个问题寻找更好的解决方案!

【问题讨论】:

    标签: c++ boost-asio


    【解决方案1】:

    你要找的是班级成员。它不是全局的,每个对象实例都有自己的副本。

    因为它也不是函数本地的,所以它会在同一个对象上的成员函数调用中保持不变。

    我认为您不需要标志,因为您可以询问流是否已经打开:

    if (!m_outfile.is_open())
        m_outfile.open(filename, std::ios::binary);
    

    由于您希望出现错误异常,请考虑在文件流上启用它们:

    m_outfile.exceptions(std::ios::failbit | std::ios::badbit);
    

    考虑使用val 的实际大小,而不是复制假定的类型(int)。

    for (auto val : number_container) {
        m_outfile.write(reinterpret_cast<const char*>(&val),
                        sizeof(val));
    }
    

    (旁白:如果number_container 是连续的,您可以更有效地将其写为:

    auto span = as_bytes(std::span(number_container));
    m_outfile.write(reinterpret_cast<char const*>(span.data()), span.size());
    

    /aside)

    这是一个现场演示On Coliru

    #include <boost/asio.hpp>
    #include <boost/bind/bind.hpp>
    #include <fstream>
    #include <iostream>
    #include <span>
    
    using namespace std::chrono_literals;
    static constexpr auto DUMP_INTERVAL = 1s;
    
    struct Data_dump {
        Data_dump() { //
            m_outfile.exceptions(std::ios::failbit | std::ios::badbit);
        }
    
        void foo() {
            try {
                if (!m_outfile.is_open())
                    m_outfile.open(m_filename, std::ios::binary);
    
                //for (auto val : number_container) {
                    //m_outfile.write(reinterpret_cast<const char*>(&val),
                                    //sizeof(val));
                //}
                auto span = as_bytes(std::span(number_container));
                m_outfile.write(reinterpret_cast<char const*>(span.data()), span.size());
                m_outfile.flush();
    
                m_timer.expires_at(m_timer.expiry() + DUMP_INTERVAL);
                m_timer.async_wait(boost::bind(&Data_dump::dump, this, boost::placeholders::_1));
                std::cerr << "Expiry: " << (m_timer.expiry() - std::chrono::steady_clock::now())/1ms << "ms
    ";
            } catch (std::exception const& e) {
                throw std::runtime_error("Error in writing to " + m_filename);
            }
        }
    
        void run() {
            ioc.run_for(10s); // for COLIRU
        }
    
    private:
        void dump(boost::system::error_code ec) {
            // don't use rand in production code!
            std::cerr << "Dump (" << ec.message() << ")" << std::endl;
            generate(begin(number_container), end(number_container), std::rand);
    
            foo();
        }
    
        std::string const         m_filename = "numbers.bin";
        boost::asio::io_context   ioc;
        boost::asio::steady_timer m_timer{ioc, 1s};
        std::ofstream             m_outfile;
        std::vector<int>          number_container{1, 2, 3};
    };
    
    int main() {
        boost::asio::io_context ioc;
        Data_dump dd;
    
        dd.foo();
        dd.run();
    
    }
    

    显示时间:

    奖金

    在您的情况下,看起来您实际上可能不需要异步 IO(您没有在显示的代码中使用它),所以也许只是这样写:

    void foo(std::string const& filename) {
        std::ofstream ofs;
        ofs.exceptions(std::ios::failbit | std::ios::badbit);
        ofs.open(filename, std::ios::binary);
    
        auto start = std::chrono::steady_clock::now();
        for (auto now = start; now <= start + 10s; now += DUMP_INTERVAL) {
            /*
             *for (auto val : data)
             *    ofs.write(reinterpret_cast<const char*>(&val), sizeof(val));
             */
    
            auto span = as_bytes(std::span(data));
            ofs.write(reinterpret_cast<char const*>(span.data()), span.size());
            ofs.flush();
    
            std::this_thread::sleep_until(now + 1s);
        }
    }
    

    现在,也许添加一个线程来更新数字容器(确保添加同步,例如使用锁或原子交换等)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-15
      • 1970-01-01
      • 2011-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-04
      相关资源
      最近更新 更多