【问题标题】:using fgets as non-blocking function c++使用 fgets 作为非阻塞函数 c++
【发布时间】:2011-05-19 08:16:29
【问题描述】:

我正在编写一个从标准输入循环读取的程序,使用函数 fgets,如下所示:

while(fgets(buffer2, BUFFERSIZE , stdin) != NULL){
  //Some code  
}

我希望我的代码是非阻塞的,即:当用户目前没有输入时,我不希望程序停留在“fgets”行。
我该怎么做?

【问题讨论】:

  • 我会使用单独的线程来做到这一点。

标签: c++ nonblocking fgets


【解决方案1】:

fgets()是一个阻塞函数,意思是等到数据可用。

如果要执行异步 I/O,可以使用 select()poll()epoll()。然后在有可用数据时从文件描述符中读取。

这些函数使用 FILE* 句柄的文件描述符,通过以下方式检索:

int fd = fileno(f);

如果您使用的是 Unix 或 Linux,那么一种解决方案是将文件使用的文件描述符标记为非阻塞。示例:

#include <fcntl.h>  
FILE *handle = popen("tail -f /als/als_test.txt", "r"); 
int fd = fileno(handle);  
flags = fcntl(fd, F_GETFL, 0); 
flags |= O_NONBLOCK; 
fcntl(fd, F_SETFL, flags); 

fgets 现在应该是非阻塞的,将返回 null 并为您设置错误代码。

【讨论】:

  • 小心非阻塞 I/O - 根据我的阅读,它适用于管道和 ttys,但不适用于常规文件,因此 ./prog &lt; foo 不会执行非阻塞 I /O。根据使用情况,这可能无关紧要。 (见:davmac.org/davpage/linux/async-io.html#nonblockingopen
  • @Thanatos 似乎非阻塞 I/O 与常规文件并不相关。在文件结束之前始终有数据可用,因此读取将返回数据或指示 EOF。
【解决方案2】:

如果你有一个正确的 POSIX 环境,你可以在调用 fgets()...read() 之前使用select()poll() 检查stdin 的描述符上的输入.

Jan 在下面的评论(谢谢!)解释了为什么你不能在这种方法中使用 fgets()... 总而言之,FILE 对象中有一个额外的缓冲层,并且数据已经可以通过select() 在文件描述符上找不到更多内容...阻止您的程序及时响应,并且如果其他系统正在等待对已发送数据的响应,然后在 stdin 上发送更多数据,则可能会挂起。

【讨论】:

  • 不够。您还必须将句柄设置为非阻塞模式(因为您总是需要在再次调用 select/poll 之前将其读取出来,并且不会阻塞)并且您需要确保始终完全读取它,因为 fgets 读取更多比返回,所以你可能仍然有数据缓冲,但 select 会等待,因为底层描述符没有更多了。
  • @Jan:是的,绝对......我很傻。这意味着 - 没有非阻塞标准输入 - 您必须使用 read() 而不是 fgets() 并自己处理行的组装(根据我的经验,这往往有点繁琐或效率低下)......跨度>
【解决方案3】:

你基本上有两种选择:

  1. 在单独的线程中运行该循环。
  2. 检查您的操作系统是否支持某些用于非阻塞 IO 的 API。

【讨论】:

  • 或 3. select()/poll() 等...? :_)
  • @Tony - 实际上,这真的取决于@Dror 需要什么以及整个代码是什么,因为如果它是一个单线程,它可能会阻塞在其他地方并且不再检查用户输入。
  • @Kiril:当然......但是在提出的问题中没有什么可以排除 3 的。干杯。
  • @Tony - 当然,你是完全正确的(:我根本没有想到这一点(:
  • @Kiril:嗯,不是那么完全...... Jan 在对我的回答的评论中纠正了我的疏忽......她走了:-)。
【解决方案4】:

这听起来有点矫枉过正,但这是我想到的。

使用 2 个不同的线程 - 一个使用此循环并等待阻塞(我认为这不能非阻塞地完成)。当读取某些内容时,将其推入管道。

同时,另一个线程会做它需要做的任何事情并不时检查管道中的数据(显然,你希望这是异步的,或者至少我是这样理解的。如果是这样,这意味着不同的线程)

但是,您需要很好地同步两个线程。你应该检查你的操作系统关于多线程和 IO 操作。

【讨论】:

    【解决方案5】:

    在 Linux 上,您可以通过按 ctrl-d 来指定输入的结束,当然您也可以使用单独的线程来完成。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      • 1970-01-01
      • 1970-01-01
      • 2020-03-29
      • 2010-10-04
      • 2015-11-15
      • 1970-01-01
      相关资源
      最近更新 更多