Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/357.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
通过管道的python进程通信:竞争条件_Python_Named Pipes_Race Condition_Python 3.2 - Fatal编程技术网

通过管道的python进程通信:竞争条件

通过管道的python进程通信:竞争条件,python,named-pipes,race-condition,python-3.2,Python,Named Pipes,Race Condition,Python 3.2,所以我有两个Python3.2进程需要相互通信。大多数需要交流的信息都是标准词典。命名管道似乎是一种方法,所以我创建了一个管道类,可以在两个进程中实例化。这个类实现了一个非常基本的获取信息的协议 我的问题是,有时有效,有时无效。除了代码失败的地方之外,这种行为似乎没有任何模式 下面是管道类的一些重要部分。如果需要更多代码,请呼喊: class Pipe: """ there are a bunch of constants set up here. I dont think it

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

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

下面是管道类的一些重要部分。如果需要更多代码,请呼喊:

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中):

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

一个示例错误是:

Not a valid line length: "KE 17"
事实上,它是间歇性的,这对我来说是由于某种比赛条件造成的,我只是在努力弄清楚它可能是什么。有什么想法吗

编辑添加了有关调用进程的内容

管道的使用方式是:通过调用具有相同路径的构造函数,在processA和ProcessB中实例化管道。然后,进程A将间歇地向管道写入数据,而进程B将尝试从中读取数据。在任何时候,我都不会试图让这个东西成为一个双向的

这里有一个更冗长的情况解释。我一直想把问题简短一点,但我想是时候放弃了。总之,我有一个守护进程和一个金字塔进程,需要好好玩。有两个正在使用的管道实例:一个只对金字塔进行写入,另一个只对守护进程进行写入。金字塔写的东西真的很短,我在这个管道上没有遇到任何错误。守护进程写的东西要长得多,这是给我带来悲伤的管道。这两个管道的实现方式相同。这两个进程只将字典写入各自的管道(如果不是这样,那么Pipe.write中将出现异常)

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

所以:只有一个管道出现了问题,有问题的管道比另一个管道的流量大得多,这是一个保证,这两个管道都只编写字典

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

在你告诉我去尝试之前…除了读下去的东西。 异常被提出的事实是困扰我的。在我看来,应该始终向它传递一个看起来像整数的字符串。大多数情况下都是这样,而不是全部。所以,如果你有强烈的欲望去评论它怎么可能不是一个整数,请不要

套用我的问题:发现种族状况,你将成为我的英雄

编辑一个小例子:

过程1.py:

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

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

试着摆脱
Try:
except:
块,看看实际抛出了什么异常

因此,将您的样本替换为:

iLineLen = int(sLineLen.strip(string.punctuation+string.whitespace))
我打赌它现在会抛出一个
ValueError
,这是因为您试图将“KE 17”转换为
int


你需要去除的不仅仅是
string.whitespace
string.标点符号
,如果你想在玩完代码后将字符串转换为
int
,我怀疑问题在于你如何读取文件

具体来说,像这样的行:

os.read(self.iFH, iLineStartBaseLength)
该调用不一定返回
iListartBaseLength
字节-它可能会消耗
“LI”
,然后返回
READLINE\u FAIL
并重试。在第二次尝试中,它将获得行的剩余部分,并以某种方式将非数字字符串提供给
int()
调用

不可预测性可能来自fifo的刷新方式——如果在写入完整行时恰好刷新,则一切正常。如果在写了一半的时候它会变红,那就是奇怪

至少在我最终使用的脚本的黑客版本中,
oP.read()
调用
进程2.py
通常会得到与发送的脚本不同的dict(其中
可能会渗入先前的
和其他奇怪之处)

我可能弄错了,因为为了让代码在OSX上运行,我不得不做了一系列的更改,并且在实验过程中做了进一步的更改

不知道该如何修复,但是。。使用
json
模块或类似模块,协议/解析可以大大简化-换行分隔的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)

很明显,引发异常是因为
“KE 17”
无法转换为整数——你知道为什么有一个进程会写这个吗?@mgilson:gosh,真的吗?我对我的问题稍加修改,也许现在会更有意义。我很确定这是竞争条件的结果,因为它是间歇性的,并且总是在同一个位置。当它失败时,它会在您最初运行
.read()
-调用过程时发生吗?或者它会在中途发生吗?此外,拥有完整版本的代码也很有用,发布的代码包含了大部分代码,但缺少
管道。。。所有常量都是同名字符串。这只是一种防止愚蠢的打字错误的方法。。。我现在正在处理缩进。它并不总是发生在第一次跑步时,有时会,有时会发生在完全不同的跑步中。没有我能确定的模式
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)