阻塞调用读取管道

Posted

技术标签:

【中文标题】阻塞调用读取管道【英文标题】:Blocking call to read a pipe 【发布时间】:2016-11-15 11:08:03 【问题描述】:

我正在使用 Qt 创建一个显示 GUI 并接受来自管道的输入的小应用程序。

如果没有创建管道(或者,据我所知,如果没有编写器),对 fopen 的调用会阻塞,甚至认为它应该是在 show() 函数之后调用的,用户界面未显示。

如何显示 UI,然后调用fopen 和相关代码?我不在乎fopen 是否会阻塞,只要我的窗口事先在屏幕上即可。

我尝试过使用 connect(this, SIGNAL(window_loaded), this, SLOT(setupListener())); 之类的东西,但行为保持不变。

有什么提示吗?谢谢!

ma​​in.cpp

#include <QApplication>

#include "metadataWindow.h"

#include <sys/time.h>
#include <sys/types.h>

int main(int argc, char *argv[])

    QApplication app(argc, argv);
    metadataWindow window;
    window.showFullScreen();
    window.setupListener();

    return app.exec();

metadataWindow.cpp

metadataWindow::metadataWindow(QWidget *parent) : QWidget(parent)

    this->setupUI(); // not shown here, but just basic QLabel stuff


void metadataWindow::setupListener()

    const char *metadata_file = "/tmp/my-pipe-file";

    // vvvvv This here is blocking vvvvvv
    FILE *fd = fopen(metadata_file, "r");

    pipe = new QTextStream(fd);

    streamReader = new QSocketNotifier(fileno(fd), QSocketNotifier::Read, qApp);
    QObject::connect(streamReader, SIGNAL(activated(int)), this, SLOT(onData()));
    streamReader->setEnabled(true);

【问题讨论】:

【参考方案1】:

X 是一个异步的、基于消息的协议。 X 显示服务器和 X 客户端程序不断地交换消息。 X 客户端程序不只是按下某种虚拟按钮,绘制它的窗口,然后每天调用它,直到它想要更改窗口上的某些内容。显示服务器和客户端程序之间唯一没有消息交换的情况是显示器上完全没有发生任何事情。没有鼠标指针移动。没有任何显示活动。

显示窗口的任务包括多个步骤,依次进行。实际的窗口对象本身被创建。所有子窗口都被创建。所有窗口都被映射。映射窗口会导致 X 服务器向客户端程序发送一系列暴露事件,作为响应,客户端程序负责渲染窗口的暴露部分。所有这些都是通过在 X 显示服务器和 X 客户端程序之间交换的数百条消息序列来完成的。

这就是QApplication::exec() 调用的作用。它进入 Qt 的主事件循环,Qt 库相应地处理 X 显示事件。在事件循环运行之前,不会有任何可见的显示变化。

在使用基于事件的基础架构(如 X/Qt)时,正确的设计模式也是基于事件的方法。您有两个基本选择。

    在新线程中执行阻塞应用程序逻辑,独立于进入 Qt 事件循环的主执行线程。这绕过并回避了遵循事件驱动设计模式的需要,并且可以在不打扰 Qt 的情况下完成普通程序所做的工作。

    也为您自己的代码使用具有非阻塞文件描述符的事件驱动模型。不能使用 fopen() 库调用。相反,管道将在非阻塞模式下为open()ed,并且当文件系统管道的另一侧打开时,该管道将可选择进行写入。阅读open()poll() 的手册页,了解更多信息。最后阅读 Qt 的 QSocketNotifier 类文档,其中解释了如何让 Qt 库也监视您自己的文件描述符上的事件,作为其主事件循环的一部分,并调用您的代码来处理读取和写入它们的任务.

当然,同时使用执行线程和套接字通知器的混合方法也是可能的。重要的一点是要了解进程应该如何正确工作,并且永远不要编写任何阻塞 Qt 主事件循环的代码。

【讨论】:

非常感谢您非常清楚的解释。我在非阻塞模式下使用open() 进行了第二条路线,因此,我在第一次调用onData() 时初始化了QTextStream 管道(因为我无法直接从open() 获得FILE*)。这实际上工作得很好,我认为它不会有任何缺点..

以上是关于阻塞调用读取管道的主要内容,如果未能解决你的问题,请参考以下文章

为啥从管道读取会阻塞进程?

从命名管道读取不会给出任何输出并无限期地阻塞代码

管道编写器可以判断阅读器何时阻塞吗?

PHP中管道的非阻塞打开

C、是不是可以阻塞进程直到管道再次打开?

如何解除阻塞已删除命名管道上的线程阻塞?