【问题标题】:Using pipes to communicate with children in multithreaded programs在多线程程序中使用管道与子进程通信
【发布时间】:2013-10-21 23:27:54
【问题描述】:

我正在尝试使用 fork 使用类似于以下代码的多线程父级执行子程序:

#include <thread>
#include <unistd.h>
#include <vector>
#include <sys/wait.h>

void printWithCat(const std::string& data) {
    std::vector<char*> commandLine;
    // exec won't change argument so safe cast
    commandLine.push_back(const_cast<char*>("cat"));
    commandLine.push_back(0);

    int pipes[2];
    pipe(pipes);
    // Race condition here
    pid_t pid = fork();
    if (pid == 0) {
        // Redirect pipes[0] to stdin
        close(pipes[1]);
        close(0);
        dup(pipes[0]);
        close(pipes[0]);
        execvp("cat", &commandLine.front());
    }
    else {
        close(pipes[0]);
        write(pipes[1], (void*)(data.data()), data.size());
        close(pipes[1]);
        waitpid(pid, NULL, 0);
    }

}

int main()
{
     std::thread t1(printWithCat, "Hello, ");
     std::thread t2(printWithCat, "World!");

     t1.join();
     t2.join();
}

此代码包含对 pipe 的调用和对 fork 的调用之间的竞争条件。如果两个线程都创建管道然后分叉,则每个子进程都包含两个管道的打开文件描述符,并且只关闭一个。结果是管道永远不会关闭,子进程永远不会退出。我目前将 pipe 和 fork 调用包装在全局锁中,但这增加了额外的同步。有没有更好的办法?

【问题讨论】:

  • 您确定存在竞争条件吗?管道是一个局部变量,因此每个线程都有自己的副本。
  • 管道是本地的,但打开的文件描述符对于进程来说是全局的。只有当所有打开的描述符都关闭时,管道才会关闭,并且分支可能会无意中创建您不知道的描述符的副本。
  • 我看到了问题,但我不知道更好的解决方案。分叉和线程不能很好地结合在一起,到目前为止我还没有听到任何解决方案。
  • 我明白了……抱歉,应该更彻底地阅读。是否可以选择使用仅在分叉后打开的命名管道?

标签: c++ multithreading posix


【解决方案1】:

不要认为通过避免代码中的锁来避免同步 - 无论如何,内核都会为进程创建获取锁,可能在比您的锁更全局的级别上。

所以继续在这里使用轻量级互斥锁。

当程序的不同部分进行fork 调用并且不同意单个互斥体(因为有些被隐藏在库代码中等)时,您的问题就会出现

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2012-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多