gpt4 book ai didi

python - gobject io监控+非阻塞读取

转载 作者:行者123 更新时间:2023-11-28 17:55:00 25 4
gpt4 key购买 nike

我在 python 中使用 io_add_watch 监视器时遇到问题(通过 gobject)。我想在每次通知后对整个缓冲区进行非阻塞读取。这是代码(缩短了一点):

class SomeApp(object):

def __init__(self):
# some other init that does a lot of stderr debug writes
fl = fcntl.fcntl(0, fcntl.F_GETFL, 0)
fcntl.fcntl(0, fcntl.F_SETFL, fl | os.O_NONBLOCK)
print "hooked", gobject.io_add_watch(0, gobject.IO_IN | gobject.IO_PRI, self.got_message, [""])
self.app = gobject.MainLoop()

def run(self):
print "ready"
self.app.run()

def got_message(self, fd, condition, data):
print "reading now"
data[0] += os.read(0, 1024)
print "got something", fd, condition, data
return True

gobject.threads_init()
SomeApp().run()

这就是诀窍 - 当我在没有激活调试输出的情况下运行程序时,我没有收到 got_message 调用。当我首先向 stderr 写入很多内容时,问题就消失了。如果除了这段代码中可见的打印之外我不写任何东西,我就不会得到标准输入消息信号。另一件有趣的事情是,当我尝试在启用 stderr 调试但通过 strace 运行同一个应用程序时(检查是否有任何我错过的 fcntl/ioctl 调用),问题再次出现。

简而言之:如果我先在没有 strace 的情况下向 stderr 写入很多内容,io_watch 会起作用。如果我用 strace 写了很多,或者根本不写,io_watch 不起作用。

“some other init”部分需要一些时间,所以如果我在看到“hooked 2”输出之前输入一些文本,然后在“ready”之后按“ctrl+c”,get_message回调被调用,但读取调用抛出 EAGAIN,因此缓冲区似乎是空的。

与标准输入相关的 Strace 日志:

ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
fcntl(0, F_GETFL) = 0xa002 (flags O_RDWR|O_ASYNC|O_LARGEFILE)
fcntl(0, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC|O_LARGEFILE) = 0
fcntl(0, F_GETFL) = 0xa802 (flags O_RDWR|O_NONBLOCK|O_ASYNC|O_LARGEFILE)

有人知道这里发生了什么吗?


编辑:另一个线索。我试图重构应用程序以在不同的线程中进行读取并通过管道将其传回。它“有点”有效:

...
rpipe, wpipe = os.pipe()
stopped = threading.Event()
self.stdreader = threading.Thread(name = "reader", target = self.std_read_loop, args = (wpipe, stopped))
self.stdreader.start()
new_data = ""
print "hooked", gobject.io_add_watch(rpipe, gobject.IO_IN | gobject.IO_PRI, self.got_message, [new_data])

def std_read_loop(self, wpipe, stop_event):
while True:
try:
new_data = os.read(0, 1024)
while len(new_data) > 0:
l = os.write(wpipe, new_data)
new_data = new_data[l:]
except OSError, e:
if stop_event.isSet():
break
time.sleep(0.1)
...

令人惊讶的是,如果我将相同的文本放入新管道,一切都会开始工作。问题是:

  • 第一行根本没有被“注意到”——我只看到第二行和后续行
  • 很丑

也许这会让其他人了解为什么会这样?

最佳答案

这听起来像是一种竞争条件,在这种情况下,设置回调会有一些延迟,或者环境发生了变化,这会影响您是否可以设置回调。

我会仔细查看调用 io_add_watch() 之前发生的情况。例如 Python fcntl 文档说:

All functions in this module take a file descriptor fd as their first argument. This can be an integer file descriptor, such as returned by sys.stdin.fileno(), or a file object, such as sys.stdin itself, which provides a fileno() which returns a genuine file descriptor.

显然,当您假设 STDIN 的 FD == 0 时,这不是您正在做的事情。我会先更改它,然后再试一次。

另一件事是,如果 FD 已经被阻塞,那么您的进程可能会等待,而其他非阻塞进程正在运行,因此存在时间差异,具体取决于您先做什么。如果重构 fcntl 使其在程序启动后立即完成,甚至在导入 GTK 模块之前完成,会发生什么情况?

我不确定我是否理解为什么使用 GTK GUI 的程序首先要从标准输入读取数据。如果你真的想捕获另一个进程的输出,你应该使用 subprocess 模块来设置一个管道,然后 io_add_watch() 在管道上像这样:

proc = subprocess.Popen(command, stdout = subprocess.PIPE)
gobject.io_add_watch(proc.stdout, glib.IO_IN, self.write_to_buffer )

同样,在此示例中,我们确保在调用 io_add_watch() 之前我们有一个有效打开的 FD。

通常,当使用 gobject.io_add_watch() 时,它会在 gobject.MainLoop() 之前被调用。例如,下面是一些使用 io_add_watch 来捕获 IO_IN 的工作代码。

关于python - gobject io监控+非阻塞读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1586342/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com