【问题标题】:simultaneous read on file descriptor from two threads从两个线程同时读取文件描述符
【发布时间】:2021-09-10 22:31:59
【问题描述】:
  1. 我的问题:在 Linux(以及 FreeBsd 中,通常在 UNIX 中)是否可以/合法地同时从两个线程读取单个文件描述符?

  2. 我做了一些搜索但一无所获,尽管很多人同时询问有关从/向套接字 fd 读取/写入的问题(意思是在其他线程正在写入时读取,在其他线程正在读取时不读取)。我也阅读了一些手册页,但对我的问题没有明确的答案。

  3. 我为什么要问它。我试图实现一个简单的程序来计算标准输入中的行数,比如 wc -l。我实际上是在测试我自制的 C++ io 引擎的开销,发现 wc 快了 1.7 倍。我削减了一些 C++ 并接近 wc 速度但没有达到它。然后我尝试了输入缓冲区的大小,对其进行了优化,但 wc 显然还是快了一点。最后我创建了 2 个并行读取相同 STDIN_FILENO 的线程,这终于比 wc 快了!但是行数变得不正确......所以我想一些垃圾来自意外的读取。内核不关心读取什么进程吗?

编辑:我做了一些研究,发现直接通过系统调用调用 read 并没有改变任何东西。内核代码似乎做了一些同步处理,但我不太了解(read_write.c)

【问题讨论】:

  • 顺便说一句:并行读取可能会让你变慢而不是更快。如果您有 I/O 瓶颈,那么您所做的就是创建锁争用。你可以专注于你的缓冲策略。
  • 但是我没有锁定,我只是读了,内核应该在内部锁定。关于缓冲:我尝试改变缓冲区大小并提出了一些最佳方案。它仍然比 wc 慢。我现在有一个理论,我需要非阻塞读取来提高性能。
  • 非阻塞读取也无济于事。嗯..
  • 非阻塞读取 + 8 页缓冲区大小 + std::count 最终击败 wc -l。没关系,这不完全是我的问题。
  • 2 线程正确锁定比最好的单线程版本快 20%。因为我只有 2 个内核,所以无法测试更多线程。

标签: multithreading posix file-descriptor


【解决方案1】:

这是未定义的行为,POSIX 说:

read() 函数应尝试从文件中读取 nbyte 字节 与打开的文件描述符 fildes 关联到缓冲区中 由 buf 指出。多个并发读取的行为 未指定相同的管道、FIFO 或终端设备。

【讨论】:

  • POSIX 必须通过观察来定义,每个操作系统。随着 SSD 的兴起,我看到更多这样大胆的并发阅读发现者到来了
  • 但是,read() 在常规文件和符号链接上的行为定义的。看我的回答。
【解决方案2】:

当与描述符 (fd) 一起使用时,read() 和 write() 依赖于 fd 的内部状态来了解将发生读取和写入的“当前偏移量”。因此,它们不是线程安全的。

为了允许多个线程同时使用单个描述符,提供了 pread() 和 pwrite()。使用这些接口,指定了描述符和所需的偏移量,因此不使用描述符中的“当前偏移量”。

【讨论】:

    【解决方案3】:

    关于同时访问单个文件描述符(即从多个线程甚至进程),我将引用POSIX.1-2008 (IEEE Std 1003.1-2008), Subsection 2.9.7 Thread Interactions with Regular File Operations

    2.9.7 线程与常规文件操作的交互

    以下所有函数在对常规文件或符号链接进行操作时,在 POSIX.1-2008 中指定的效果中相互之间应是原子的:

    […] 读() […]

    如果两个线程各自调用其中一个函数,则每个调用要么看到另一个调用的所有指定效果,要么一个都看不到。 […]

    乍一看,这看起来相当不错。但是,我希望您在操作常规文件或符号链接时不要错过限制

    @jarero 引用:

    未指定同一管道、FIFO 或终端设备上的多个并发读取的行为。

    因此,我们隐含地同意,我假设:这取决于您正在阅读的文件的类型。您说,您从 STDIN 读取。好吧,如果你的 STDIN 是一个普通文件,你可以使用并发访问。否则你不应该。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多