c++ ostreams 中的管道安全透明缓冲

Pipe-safe transparent buffering in c++ ostreams

提问人:elbrunovsky 提问时间:3/19/2023 最后编辑:elbrunovsky 更新时间:3/19/2023 访问量:36

问:

我有一对程序,我在其中编写了许多短行数据,例如 cout 和其他文件输出流,每个程序大约有 50 个字符长。涉及管道,所以我需要原子写入,我从简单地刷新每一行写入开始,但现在刷新已成为两个程序的主要瓶颈。A=123 B=456 ...data...\n

我希望我的所有输出流(包括 cout)在不拼接单个行的情况下批处理它们接收到的内容并刷新(如果新内容会溢出缓冲区,则新内容不会被拼接)。我尝试做一些事情,比如如果缓冲区很小,希望这将是实现的行为,但似乎调用没有做任何事情。<<PIPE_BUFstd::cout.rdbuf()->pubsetbuf(new char[PIPE_BUF], PIPE_BUF - 200)

实际上,我希望以下代码示例对我打开的任何其他 ostreams 都有效果;我可以调整一次流,但它必须在写入时透明地工作。cout

#include <iostream>
#include <limits.h> // PIPE_BUF
#include <random>
// #include <format>
// using std::format;
#include <fmt/format.h>
using fmt::format;

unsigned SAFE_BUF = PIPE_BUF - 200;
bool flushing = false;

int main() {
    unsigned pending = 0;
    for (int l = 0; l < 300'000; l++) {
        int A = rand() % 1000, B = rand() % 1000, L = rand() % 40, c = rand() % 26;
        std::string text = format("A={} B={} {}\n", A, B, std::string(L, 'a' + c));
        if (pending + text.size() >= SAFE_BUF) std::cout.flush(), pending = 0;
        std::cout << text, pending += text.size();
        if (flushing) std::cout.flush(), pending = 0;
    }
    return 0;
}
#!/bin/bash
rm -f test.log
for _ in 0 1 2 3 4 5 6 7; do ./writer >>test.log & done && wait
grep -q --invert-match "^A" test.log && echo MANGLED || echo OK
C++ 缓冲区 IOSTREAM

评论

0赞 Sam Varshavchik 3/19/2023
“刷新每一行写入”,这就是“原子写入”的定义。如果这被证明是一个“瓶颈”,那么下一步就是回到绘图板,并想出一个不同的方法。再多的 streambuf 摆弄也无法改变这一点。
0赞 elbrunovsky 3/19/2023
我非常清楚地表达了我想要什么,我在这里展示的代码从字面上解决了我的问题,我只是希望它透明地工作。如果答案是“它不能透明地工作”,那是一个不错的解决方案。去惹恼别人,请@SamVarshavchik
0赞 Sam Varshavchik 3/19/2023
我最谦卑的道歉,只是我不知道“透明工作”是什么意思。你能说得更具体一点吗?
0赞 Fabian Keßler 3/19/2023
我认为您应该查看 en.cppreference.com/w/cpp/header/syncstream,可能还有 spdlog,以检查其中之一是否符合您的需求。如果没有,您可能需要编写自己的流或手动缓冲这些消息。以 1 秒的间隔冲洗它们。
0赞 elbrunovsky 3/19/2023
@SamVarshavchik我只想将待处理的逻辑隐藏在 cout 后面的某个地方(以及我打开的其他 ostream),例如,我可以在 main 的开头以某种方式设置它。如果我必须将它包装在一个函数中,那么我总是必须使用该函数,这对于像 cout 这样的东西来说远非理想。

答: 暂无答案