从父进程访问Python Multiprocessing.Process子类的状态

从父进程访问Python Multiprocessing.Process子类的状态,python,parallel-processing,network-programming,multiprocessing,Python,Parallel Processing,Network Programming,Multiprocessing,我正在创建一个简单的TCP服务器作为存根,这样我就可以测试一个操作一个测试设备的脚本,而不必在那里安装设备。服务器应该坐在那里等待连接,然后维护和更新状态变量(只有6个整数的列表),以响应它接收到的命令。然后,父进程(例如单元测试类)应该能够随时查询状态 服务器的界面应尽可能简单: server = StubServer() server.start() ''' the client script connects with the server and some stuff happens t

我正在创建一个简单的TCP服务器作为存根,这样我就可以测试一个操作一个测试设备的脚本,而不必在那里安装设备。服务器应该坐在那里等待连接,然后维护和更新状态变量(只有6个整数的列表),以响应它接收到的命令。然后,父进程(例如单元测试类)应该能够随时查询状态

服务器的界面应尽可能简单:

server = StubServer()
server.start()
'''
the client script connects with the server and
some stuff happens to change the state
'''
newState = server.getState() # newState = [93,93,93,3,3,45] for example
server.terminate()
我已经将Multiprocessing.Process子类化了,这样我就可以启动服务器了,没有问题。当我第一次测试它时,在getState()方法中,我只是返回了实例变量_state,但我发现它始终只是初始状态。经过一番挖掘,我找不到任何类似的例子。很多关于子类化过程,但不是这个特定的问题。最后,我把下面的代码放在一起,它使用一个内部队列()来存储状态,但在我看来,这看起来很混乱和笨拙。有更好的方法吗

import socket
from multiprocessing import Process, Queue

class StubServer(Process):

    _port = 4001
    _addr = '' # all addresses 0.0.0.0
    _sock = None
    _state = []
    _queue = None

    def __init__(self, initState=[93,93,93,93,93,93]):
        super(StubServer, self).__init__()
        self._queue = Queue()
        self._state = initState

    def run(self):
        # Put the state into the queue
        self._queue.put(self._state)
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._sock.bind((self._addr, self._port))
        self._sock.listen(1)

        waitingForConnection = True
        '''
        Main loop will continue until the connection is terminated. if a connection is closed, the loop returns
        to the start and waits for a new connection. This means multiple tests can be run with the same server
        '''
        while 1:
            # Wait for a connection, or go back and wait for a new message (if a connection already exists)
            if waitingForConnection:
                waitingForConnection = False
                conn, addr = self._sock.accept()
            chunk = ''
            chunks = []
            while '\x03' not in chunk: # '\x03' is terminating character for a message
                chunk = conn.recv(8192)
                if not chunk: # Connection terminated, start the loop again and wait for a new connection
                    waitingForConnection = True
                    break
                chunks.append(chunk)
            message = ''.join(chunks)
            # Now do some stuff to parse the message, and update the state if we received a command
            if isACommand(message):
                _updateState(message)
        conn.close()
        return

    def getState(self):
        # This is called from the parent process, so return the object on the queue
        state = self._queue.get()
        # But put the state back in the queue again so it's there if this method is called again before a state update
        self._queue.put(state)
        return state

    def _updateState(self, message):
        # Do some stuff to figure out what to update then update the state
        self._state[updatedElementIndex] = updatedValue
        # Now empty the queue and put the new state in the queue
        while not self._queue.empty():
            self._queue.get()
        self._queue.put(self._state)
        return

顾名思义,
多处理
使用不同的进程。在某个时刻,调用,子进程复制父进程的内存,子进程保留自己的内存,而不是与父进程共享

不幸的是,您必须在进程之间共享内存,这导致了您提到的代码开销


您可以使用共享内存查找其他并行处理方法,但请记住,在线程/进程/节点/etc之间共享内存绝非易事。

顾名思义,
多处理使用不同的进程。在某个时刻,调用,子进程复制父进程的内存,子进程保留自己的内存,而不是与父进程共享

不幸的是,您必须在进程之间共享内存,这导致了您提到的代码开销


您可以使用共享内存寻找其他方法来进行并行处理,但请记住,在线程/进程/节点/etc之间共享内存绝非易事。

您可以随时将存根服务器的状态转储到文件,并从unittests中读取它。对于测试需求来说,这是一个非常简单的解决方案

您需要做的一切:

  • filename
    作为参数传递给构造函数
  • 使用init值调用
    \u updateState
  • 重写
    \u updateState
    以将状态写入
    文件名
    。最好在
    filename
    附近创建一个新文件并替换它。如果你担心原子性

您可以随时将存根服务器的状态转储到文件中,并从unittests读取它。对于测试需求来说,这是一个非常简单的解决方案

您需要做的一切:

  • filename
    作为参数传递给构造函数
  • 使用init值调用
    \u updateState
  • 重写
    \u updateState
    以将状态写入
    文件名
    。最好在
    filename
    附近创建一个新文件并替换它。如果你担心原子性

    • 谢谢Felipe,我的问题主要是“有没有比使用队列更好的方法?”正如我在问题中所做的那样。经过进一步研究(由于您提到共享内存),我发现对于这种情况,共享阵列要好得多:

      import socket
      from multiprocessing import Process, Array
      
      class StubServer(Process):
      
          _port = 4001
          _addr = '' # all addresses 0.0.0.0
          _sock = None
          _state = None
          _queue = None
      
          def __init__(self, initState=[93,93,93,93,93,93]):
              super(StubServer, self).__init__()
              self._state = Array('i', initState) # Is always a 6 element array
      
          def run(self):
              self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
              self._sock.bind((self._addr, self._port))
              self._sock.listen(1)
      
              waitingForConnection = True
              '''
              Main loop will continue until process is terminated. if a connection is closed, the loop returns
              to the start and waits for a new connection. This means multiple tests can be run with the same server
              '''
              while 1:
                  # Wait for a connection, or go back and wait for a new message (if a connection already exists)
                  if waitingForConnection:
                      waitingForConnection = False
                      conn, addr = self._sock.accept()
                  chunk = ''
                  chunks = []
                  while '\x03' not in chunk: # '\x03' is terminating character for a message
                      chunk = conn.recv(8192)
                      if not chunk: # Connection terminated, start the loop again and wait for a new connection
                          waitingForConnection = True
                          break
                      chunks.append(chunk)
                  message = ''.join(chunks)
                  # Now do some stuff to parse the message, and update the state if we received a command
                  if isACommand(message):
                      _updateState(message)
              conn.close()
              return
      
          def getState(self):
              # Aquire the lock return the contents of the shared array
              with self._state.get_lock():
                  return self._state[:6] # This is OK because we know it is always a 6 element array
              return state
      
          def _updateState(self, message):
              # Do some stuff to figure out what to update then..
              # Aquire the lock and update the appropriate element in the shared array
              with self._state.get_lock():
                  self._state[updatedElementIndex] = updatedValue
              return
      

      这是一种享受,更优雅一点。谢谢你的帮助

      谢谢Felipe,我的问题主要是“有没有比使用队列更好的方法?”正如我在问题中所做的那样。经过进一步研究(由于您提到共享内存),我发现对于这种情况,共享阵列要好得多:

      import socket
      from multiprocessing import Process, Array
      
      class StubServer(Process):
      
          _port = 4001
          _addr = '' # all addresses 0.0.0.0
          _sock = None
          _state = None
          _queue = None
      
          def __init__(self, initState=[93,93,93,93,93,93]):
              super(StubServer, self).__init__()
              self._state = Array('i', initState) # Is always a 6 element array
      
          def run(self):
              self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
              self._sock.bind((self._addr, self._port))
              self._sock.listen(1)
      
              waitingForConnection = True
              '''
              Main loop will continue until process is terminated. if a connection is closed, the loop returns
              to the start and waits for a new connection. This means multiple tests can be run with the same server
              '''
              while 1:
                  # Wait for a connection, or go back and wait for a new message (if a connection already exists)
                  if waitingForConnection:
                      waitingForConnection = False
                      conn, addr = self._sock.accept()
                  chunk = ''
                  chunks = []
                  while '\x03' not in chunk: # '\x03' is terminating character for a message
                      chunk = conn.recv(8192)
                      if not chunk: # Connection terminated, start the loop again and wait for a new connection
                          waitingForConnection = True
                          break
                      chunks.append(chunk)
                  message = ''.join(chunks)
                  # Now do some stuff to parse the message, and update the state if we received a command
                  if isACommand(message):
                      _updateState(message)
              conn.close()
              return
      
          def getState(self):
              # Aquire the lock return the contents of the shared array
              with self._state.get_lock():
                  return self._state[:6] # This is OK because we know it is always a 6 element array
              return state
      
          def _updateState(self, message):
              # Do some stuff to figure out what to update then..
              # Aquire the lock and update the appropriate element in the shared array
              with self._state.get_lock():
                  self._state[updatedElementIndex] = updatedValue
              return
      
      这是一种享受,更优雅一点。谢谢你的帮助