繁体   English   中英

在多线程程序中捕获标准输出

[英]Capture stdout in multithreaded program

我有一个需要从我无法控制的第三方库中调用的函数。 该函数评估我传入的命令并将其结果打印到stdout 在我的用例中,我需要将结果捕获到 std::string 变量中(而不是写入文件),我可以在单线程示例中很好地做到这一点:

int fd[2];
pid_t pid;

char *args[] = {};
if ( pid == 0 )
{
    dup2( fd[1], STDOUT_FILENO );
    close( fd[0] );
    close( fd[1] );

    char *args[] = {};

    // This func will print the results I want to stdout, but I have no control over its code.
    festival_eval_command("(print utt2)");

    execv( args[0], args );
}

close( fd[1] );
char buffer[1000000];
ssize_t length = read( fd[0], buffer, sizeof(buffer) - 1 );
std::string RESULT = buffer;
memset(buffer, 0, sizeof buffer); // clear the buffer

// RESULT now holds the contents that would have been printed out in third_party_eval().

一些限制/细节:

  • 我的程序是多线程的,所以其他线程可能同时使用stdout (我的理解是 C++ 将多个线程的输出绑定到stdout
  • 第三方库是Festival ,一个用 LISP 编写的开源语音合成库(我没有经验)。 我通过调用来使用它的 C++ API: festival_eval_command("(print utt2)");
  • festival_eval_command似乎使用stdout ,而不是std::cout (我已经通过在单线程程序中重定向两者进行了测试,并且只有stdout重定向捕获了来自utt2的输出)
  • 据我所知, festival_eval_command不允许使用备用文件描述符。
  • 此函数仅在我的多线程程序的一个线程中运行,因此我只关心将festival_eval_command输出与其他线程的stdout隔离开来。

我的问题:有没有一种方法可以安全地从多线程程序中的 stdout 中检索festival_eval_command()结果? 听起来我的选择是:

  • 在一个单独的进程中启动这个函数,它有自己的stdout 在那个单独的进程中进行 IO 重定向,获取我需要的输出并将其返回到我的主程序进程。 这样对吗? 我该怎么做呢?
  • festival_eval_command周围使用互斥锁。 我不太明白互斥体如何与其他线程交互。 如果我有这个例子:
void do_stuff_simultaneously() {
    std::cout << "Printing output to terminal..." << std::endl;
}

// main thread
void do_stuff() {
    // launch a separate thread that may print to stdout
    std::thread t(do_stuff_simultaneously);

    // lock stdout somehow

    // redirect stdout to string variable
    festival_eval_command("(print utt2)");

    // unlock stdout
}

stdout 的锁定是否会阻止do_stuff_simultaneously访问它? 有没有办法像这样使 stdout 线程安全?

但是,我的程序是多线程的,所以其他线程可能同时使用stdout

线程的输出将以您无法控制的方式交错。 除非每个线程使用一个std::cout.write写入其整个输出(原因见下文)。

有没有一种方法可以安全地从多线程程序中的stdout检索third_party_eval()结果?

每个线程必须在一个单独的进程中运行该函数,从中您可以将其stdout捕获到std::string s (每个进程不同的一个)。

然后在父进程中,您使用std::cout.write(s.data(), s.size())将该std::string写入stdout std::cout.write锁定互斥锁(以在多个线程以任何方式写入其中时保护自身免受数据竞争和损坏,包括operator<< ),以便一个进程的输出不会与其他任何内容交错。

预先注意:这说明了为什么全局变量通常是个坏主意! 更重要的是,库代码(即用于在不同上下文中重用的代码)永远不应该使用全局变量。 这也是告诉该代码的供应商的事情,他们应该修复他们的库以提供一个至少采用输出文件描述符而不是写入stdout

这是我会考虑做的事情:将整个函数执行移动到一个单独的进程。 这样,如果多个线程需要运行它,它们将启动具有单独输出的单独进程,它们可以独立处理这些输出。

另一种方法是包装这个单一的功能。 此包装器执行所有 IO 重定向,并且它(作为关键部分)由互斥锁保护,因此调用包装器的两个线程将被序列化。 但是,这也有缺点,因为与此同时,该代码仍然与您的流程的标准流混淆(因此,对输出某些内容的杂散调用会混入函数输出中)。

第二种选择是将函数放入一个包装进程,其唯一目标是序列化函数的使用。 您可以按需或在应用程序启动时启动该过程,并使用某种形式的 IPC 与其进行通信。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM