【问题标题】:Piping and forking in pythonpython中的管道和分叉
【发布时间】:2013-11-10 14:18:28
【问题描述】:

我正在尝试在 python 中编写一个程序,该程序通过父函数将 2 个整数写入管道,然后通过子函数读取这些相同的整数。然后子函数应该打印出两者的乘积。

问题是,当我运行它时,它会像预期的那样等待 5 秒,然后返回值 441 而不是 2。

希望有人可以帮助弄清楚如何修复此代码:)

import os,time

def child(pipein):
    while True:
        num1 = int(os.read(pipein,32))
        num2 = int(os.read(pipein,32))
        r=(num1)*(num2)
        print(r)
        os._exit(0)

def parent():
    pipein,pipeout = os.pipe()
    x=5
    if os.fork()==0:
        child(pipein)
    else:
        while True:
            num1=str(2)
            num2=str(1)
            line=os.write(pipeout,num1.encode())
            line=os.write(pipeout,num2.encode())
            time.sleep(x)   

parent()

【问题讨论】:

  • 如果您希望有人帮助您,您需要减少问题。您应该尝试解释它是如何不工作的,您期望它的行为方式以及您的推理原因。您仍然可能会得到答案,但线程问题非常重要,并且期望有人为您完成繁重的工作不会让您受到这个社区的喜爱。
  • 您的新版本在第一对reads 之后退出子版本,因此在打印第一个值后它将永远阻塞。

标签: python fork piping


【解决方案1】:

直接的问题是您的孩子有一个无限循环,在做任何事情之前,他会一遍又一遍地阅读num1(或者,更确切地说,阅读它两次,然后在永远不会出现的第三个输入上永远阻塞)。

通过将更多代码移入while 循环来解决此问题,如下所示:

def child(pipein):
    while True:
        num1 = int(os.read(pipein,32))
        num2 = int(os.read(pipein,32))
        r=(num1)*(num2)
        print(r)

你还不如删除os._exit(0),因为无论如何你永远也达不到它。


您的下一个问题是您的编码和解码步骤不匹配。只要你的 sys.getdefaultencoding() 是 ASCII 的严格超集(或者,实际上,只要它的数字与 ASCII 数字匹配),你就可以逃脱,但你真的不应该默默地依赖它。


接下来,os.read(pipein,32) 可以为您提供单次写入的结果,也可以为您提供最多 32 次单独写入组合在一起的结果。 write(最多 PIPE_BUF)保证是原子的这一事实对您没有帮助 - 它只是意味着您不能以写入的前半部分而不是后半部分结束。

因此,您很可能会在 num1 中获得 21,然后 5 秒后在 num2 中获得另一个 21,因此您不是每 5 秒打印一次 2,而是每 10 秒打印一次441。但即使这样也不能保证。

管道,如 TCP 套接字,是byte streams, not message streams。这意味着您需要构建某种协议。


这里有两个非常明显的选择。

首先,既然您已经在读取(最多)32 字节的固定记录大小,为什么不写入正好为 32 字节的固定记录大小呢?只需将您的 str 行更改为生成正好为 32 个字符的字符串的内容,该字符串将在任何已经有效的编码中编码为 32 个字节,并将解析为适当值的单个整数。像这样:

num1 = format(2, '32d')
num2 = format(1, '32d')

另外,每条记录可以是一对以空格分隔的数字,并且记录可以用换行符分隔。这很容易解析。特别是因为你没有使用非阻塞管道或任何东西,所以你可以在它们周围放置一个文件对象,让一切变得简单。

【讨论】:

  • 非常感谢!!现在更有意义了。实际上我几周前才开始学习 python,您的解决方案对我帮助很大!
【解决方案2】:

这就是我的想法;

from __future__ import print_function  #(1)
import os  #(2)
import sys
import time


def child(pipein):  # (3)
    num1 = int(os.read(pipein, 32))  # (6)
    num2 = int(os.read(pipein, 32))
    r = num1 * num2
    print("r = {}".format(r))
    print("Child says bye.") 
    sys.stdout.flush()  # (4)
    os._exit(0)  # (5)


def parent():
    pipein, pipeout = os.pipe()
    x = 1
    if os.fork() == 0:
        print("Launching child")
        child(pipein)
    else:  # (7)
        print("In parent")
        num1 = str(2)  # (8)
        num2 = str(1)
        os.write(pipeout, num1)
        os.write(pipeout, num2)
        print("Parent goes to sleep")
        time.sleep(x)
        print("Parent says bye.")


if __name__ == '__main__':
    parent()
  1. 这是使print() 函数在 Python 2.x 中工作所必需的
  2. 样式:每个导入都应该在单独的行中
  3. 如果您在第一次迭代后要去exit(),则无需循环
  4. 否则打印件可能不会显示。
  5. 样式:在函数后使用两个空行。
  6. 样式:在逗号后使用空格,除非它位于行尾。
  7. 删除 while 循环,否则将进入无限循环。
  8. 样式:在运算符周围使用空格。

通过这些修改,我得到以下输出;

In parent
Launching child
Parent goes to sleep
r = 2
Child says bye.
Parent says bye.

如果您想使用多个进程,通常最好使用multiprocessing 模块。它具有进程和进程池的对象,以及队列和管道等通信对象。队列是先进先出的,并且是同步的;所以如果你把两个项目放进去,你就可以读到两个项目。

【讨论】:

  • 如果你想在 Python 2 中完成这项工作,你肯定不想在 str 上调用 encode
  • 更重要的是,这并没有真正解决问题,而是引入了一个问题(父母可以在孩子还在跑步的时候退出)。如果第一个 read 碰巧抓住了整个 '21'(在大多数平台上通常会这样),孩子将永远不会打印任何东西。
  • 非常感谢您的帮助!实际上我从您的解决方案中学到了很多东西 :)
  • @abarnert str.encode() 在 python 2 中工作正常。我同意它没用,因为没有提供编码。所以它会使用默认的字符串编码。
  • @RolandSmith:除非您的代码接受 8 位输入(只有少数特殊情况,如 bz2 会这样做),否则 str.encode “工作”首先静默 decode(sys.getdefaultencoding())-ing 然后在结果上做你的encode。同时,在您编辑的版本中没有encode,它是Python 3.x 中的TypeError,因为os.write 采用bytes,而不是str。真的,你为什么要强迫 Python 3 新手首先编写兼容 Python 2.x 的代码?
猜你喜欢
  • 2010-12-14
  • 2013-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-13
  • 1970-01-01
  • 2018-05-26
相关资源
最近更新 更多