gpt4 book ai didi

python 通过管道进行进程通信 : Race condition

转载 作者:太空宇宙 更新时间:2023-11-04 06:34:33 26 4
gpt4 key购买 nike

所以我有两个Python3.2进程需要相互通信。大多数需要交流的信息都是标准词典。命名管道似乎是可行的方法,所以我制作了一个可以在两个进程中实例化的管道类。这个类实现了一个非常基本的协议(protocol)来获取信息。

我的问题是它有时有效,有时无效。除了代码失败的地方外,此行为似乎没有任何模式。

以下是 Pipe 类中重要的部分。如果你想要更多代码,请大声喊:

class Pipe:
"""
there are a bunch of constants set up here. I dont think it would be useful to include them. Just think like this: Pipe.WHATEVER = 'WHATEVER'
"""
def __init__(self,sPath):
"""
create the fifo. if it already exists just associate with it
"""
self.sPath = sPath
if not os.path.exists(sPath):
os.mkfifo(sPath)
self.iFH = os.open(sPath,os.O_RDWR | os.O_NONBLOCK)
self.iFHBlocking = os.open(sPath,os.O_RDWR)

def write(self,dMessage):
"""
write the dict to the fifo
if dMessage is not a dictionary then there will be an exception here. There never is
"""
self.writeln(Pipe.MESSAGE_START)
for k in dMessage:
self.writeln(Pipe.KEY)
self.writeln(k)
self.writeln(Pipe.VALUE)
self.writeln(dMessage[k])
self.writeln(Pipe.MESSAGE_END)

def writeln(self,s):
os.write(self.iFH,bytes('{0} : {1}\n'.format(Pipe.LINE_START,len(s)+1),'utf-8'))
os.write(self.iFH,bytes('{0}\n'.format(s), 'utf-8'))
os.write(self.iFH,bytes(Pipe.LINE_END+'\n','utf-8'))

def readln(self):
"""
look for LINE_START, get line size
read until LINE_END
clean up
return string
"""
iLineStartBaseLength = len(self.LINE_START)+3 #'{0} : '
try:
s = os.read(self.iFH,iLineStartBaseLength).decode('utf-8')
except:
return Pipe.READLINE_FAIL

if Pipe.LINE_START in s:
#get the length of the line
sLineLen = ''
while True:
try:
sCurrent = os.read(self.iFH,1).decode('utf-8')
except:
return Pipe.READLINE_FAIL
if sCurrent == '\n':
break
sLineLen += sCurrent
try:
iLineLen = int(sLineLen.strip(string.punctuation+string.whitespace))
except:
raise Exception('Not a valid line length: "{0}"'.format(sLineLen))
#read the line
sLine = os.read(self.iFHBlocking,iLineLen).decode('utf-8')

#read the line terminator
sTerm = os.read(self.iFH,len(Pipe.LINE_END+'\n')).decode('utf-8')
if sTerm == Pipe.LINE_END+'\n':
return sLine
return Pipe.READLINE_FAIL

else:
return Pipe.READLINE_FAIL

def read(self):
"""
read from the fifo, make a dict
"""
dRet = {}
sKey = ''
sValue = ''
sCurrent = None

def value_flush():
nonlocal dRet, sKey, sValue, sCurrent
if sKey:
dRet[sKey.strip()] = sValue.strip()
sKey = ''
sValue = ''
sCurrent = ''

if self.message_start():
while True:
sLine = self.readln()
if Pipe.MESSAGE_END in sLine:
value_flush()
return dRet
elif Pipe.KEY in sLine:
value_flush()
sCurrent = Pipe.KEY
elif Pipe.VALUE in sLine:
sCurrent = Pipe.VALUE
else:
if sCurrent == Pipe.VALUE:
sValue += sLine
elif sCurrent == Pipe.KEY:
sKey += sLine
else:
return Pipe.NO_MESSAGE

它有时会在这里失败(在 readln 中):

        try:
iLineLen = int(sLineLen.strip(string.punctuation+string.whitespace))
except:
raise Exception('Not a valid line length: "{0}"'.format(sLineLen))

它不会在其他任何地方失败。

一个示例错误是:

Not a valid line length: "KE 17"

它断断续续的事实告诉我这是由于某种竞争条件造成的,我只是在努力弄清楚它可能是什么。有什么想法吗?

EDIT 添加了关于调用进程的内容

Pipe 的使用方法是在 processA 和 ProcessB 中通过调用具有相同路径的构造函数来实例化它。然后进程 A 将间歇性地写入管道,进程 B 将尝试从中读取。在任何时候我都不会尝试让这件事成为双向的。

这是对这种情况的更冗长的解释。我一直试图让问题简短,但我认为是时候放弃这个问题了。 Anyhoo,我有一个守护进程和一个金字塔进程需要很好地发挥作用。有两个 Pipe 实例在使用中:一个只有 Pyramid 写入,一个只有守护进程写入。 Pyramid 写的东西真的很短,我在这个管道上没有遇到任何错误。守护进程写的东西要长得多,这是让我悲伤的管道。两个管道的实现方式相同。两个进程都只将字典写入各自的 Pipes(如果不是这种情况,那么 Pipe.write 中就会出现异常)。

基本算法是:Pyramid 产生守护进程,守护进程加载疯狂的对象层次结构和大量的 ram 消耗。 Pyramid 将 POST 请求发送到守护进程,然后守护进程进行一大堆计算并将数据发送到 Pyramid,以便呈现一个人性化的页面。然后,人类可以通过填写 HTML 表单等方式响应层次结构中的内容,从而导致金字塔向守护进程发送另一个字典,然后守护进程发回字典响应。

因此:只有一个管道出现问题,问题管道的流量比另一个管道多得多,并且可以保证只向其中一个管道写入字典

编辑作为对问题和评论的回应

在你告诉我尝试之前...except 继续阅读。完全引发异常的事实困扰着我。 iLineLengh = int(stuff) 在我看来应该始终传递一个看起来像整数的字符串。这只是大部分时间,而不是全部。因此,如果您想要评论它可能不是整数,请不要评论。

套用我的问题:发现竞争条件,你将成为我的英雄。

编辑一个小例子:

process_1.py:

oP = Pipe(some_path)
while 1:
oP.write({'a':'foo','b':'bar','c':'erm...','d':'plop!','e':'etc'})

process_2.py:

oP = Pipe(same_path_as_before)
while 1:
print(oP.read())

最佳答案

在尝试了代码之后,我怀疑问题出在您读取文件的方式上。

具体来说,像这样的行:

os.read(self.iFH, iLineStartBaseLength)

该调用不一定返回 iLineStartBaseLength 字节 - 它可能会消耗 "LI" ,然后返回 READLINE_FAIL 并重试。在第二次尝试时,它将获取该行的剩余部分,并以某种方式最终将非数字字符串提供给 int() 调用

不可预测性可能来自于刷新 fifo 的方式 - 如果在写入完整行时恰好刷新,则一切正常。如果它在该行写到一半时刷新,那就奇怪了。

至少在我最终得到的脚本的破解版本中,process_2.py 中的 oP.read() 调用经常得到一个不同的指令发送的那个(KEY 可能会渗入之前的 VALUE 和其他奇怪的地方)。

我可能是错的,因为我必须进行大量更改才能使代码在 OS X 上运行,并且在进行试验时会做进一步的更改。 My modified code here

不确定如何修复它,但是..使用 json 模块或类似模块,可以大大简化协议(protocol)/解析 - 换行符分隔的 JSON 数据更容易解析:

import os
import time
import json
import errno


def retry_write(*args, **kwargs):
"""Like os.write, but retries until EAGAIN stops appearing
"""

while True:
try:
return os.write(*args, **kwargs)
except OSError as e:
if e.errno == errno.EAGAIN:
time.sleep(0.5)
else:
raise


class Pipe(object):
"""FIFO based IPC based on newline-separated JSON
"""

ENCODING = 'utf-8'

def __init__(self,sPath):
self.sPath = sPath
if not os.path.exists(sPath):
os.mkfifo(sPath)

self.fd = os.open(sPath,os.O_RDWR | os.O_NONBLOCK)
self.file_blocking = open(sPath, "r", encoding=self.ENCODING)

def write(self, dmsg):
serialised = json.dumps(dmsg) + "\n"
dat = bytes(serialised.encode(self.ENCODING))

# This blocks until data can be read by other process.
# Can just use os.write and ignore EAGAIN if you want
# to drop the data
retry_write(self.fd, dat)

def read(self):
serialised = self.file_blocking.readline()
return json.loads(serialised)

关于python 通过管道进行进程通信 : Race condition,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13124235/

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