我有一个关于 Python 和运行交互式模拟的相当高级别的问题。这是设置:
我正在将我最初在 Smalltalk (VW) 中编写的一些模拟软件移植到 Python。它是一种从图形界面交互控制的循环神经网络。除了控制模拟本身(启动、停止等)之外,该界面还允许实时操作大多数网络参数。在最初的 Smalltalk 实现中,我有两个进程以不同的优先级运行:
两个进程之间的通信很简单,因为所有 Smalltalk 进程共享相同的地址空间(对象内存)。
我现在开始意识到在 Python 中复制类似的设置并不是那么简单。据我所知,线程模块不允许其线程共享地址空间。多处理模块可以,但方式相当复杂(使用队列等)。
所以我开始认为我的 Smalltalk 观点让我误入歧途,我完全从错误的角度处理了一个相对简单的问题。问题是,我不知道什么是正确的角度!你会建议我如何解决这个问题?我对 Python 相当陌生(显然)并且非常愿意学习。但我非常感谢有关如何构建问题以及我应该深入研究哪些多处理模块(如果有的话!)的建议。
谢谢,
斯特凡诺
最佳答案
我将就如何解决这个问题提出我的看法。内multiprocessing
模块 Pipe
和 Queue
IPC 机制确实是最好的方法;尽管您提到了增加的复杂性,但值得学习它们的工作原理。 Pipe
相当简单,所以我将用它来说明。
这是代码,然后是一些解释:
import sys
import os
import random
import time
import multiprocessing
class computing_task(multiprocessing.Process):
def __init__(self, name, pipe):
# call this before anything else
multiprocessing.Process.__init__(self)
# then any other initialization
self.name = name
self.ipcPipe = pipe
self.number1 = 0.0
self.number2 = 0.0
sys.stdout.write('[%s] created: %f\n' % (self.name, self.number1))
# Do some kind of computation
def someComputation(self):
try:
count = 0
while True:
count += 1
self.number1 = (random.uniform(0.0, 10.0)) * self.number2
sys.stdout.write('[%s]\t%d \t%g \t%g\n' % (self.name, count, self.number1, self.number2))
# Send result via pipe to parent process.
# Can send lists, whatever - anything picklable.
self.ipcPipe.send([self.name, self.number1])
# Get new data from parent process
newData = self.ipcPipe.recv()
self.number2 = newData[0]
time.sleep(0.5)
except KeyboardInterrupt:
return
def run(self):
sys.stdout.write('[%s] started ... process id: %s\n'
% (self.name, os.getpid()))
self.someComputation()
# When done, send final update to parent process and close pipe.
self.ipcPipe.send([self.name, self.number1])
self.ipcPipe.close()
sys.stdout.write('[%s] task completed: %f\n' % (self.name, self.number1))
def main():
# Create pipe
parent_conn, child_conn = multiprocessing.Pipe()
# Instantiate an object which contains the computation
# (give "child process pipe" to the object so it can phone home :) )
computeTask = computing_task('foo', child_conn)
# Start process
computeTask.start()
# Continually send and receive updates to/from the child process
try:
while True:
# receive data from child process
result = parent_conn.recv()
print "recv: ", result
# send new data to child process
parent_conn.send([random.uniform(0.0, 1.0)])
except KeyboardInterrupt:
computeTask.join()
parent_conn.close()
print "joined, exiting"
if (__name__ == "__main__"):
main()
我已经将要完成的计算封装在一个派生自
Process
的类中。 .在大多数情况下,这并不是绝对必要的,但可以使代码更易于理解和扩展。在主进程中,您可以使用 start()
开始您的计算任务。此类实例上的方法(这将启动一个单独的进程来运行对象的内容)。如您所见,我们使用
Pipe
在父进程中创建两个连接器(管道的“末端”)并将一个给 child ,而 parent 持有另一个。这些连接器中的每一个都是持有端的进程之间的双向通信机制,send()
和 recv()
做他们名字所暗示的事情的方法。在本例中,我使用管道传输数字和文本列表,但通常您可以发送列表、元组、对象或任何可腌制的东西(即可以使用 Python 的腌制工具进行序列化)。因此,您对在进程之间来回发送的内容有一定的自由度。所以你设置你的连接器,调用
start()
在你的新流程上,你正在计算。在这里,我们只是将两个数字相乘,但您可以看到它是在子进程中“交互地”完成的,并且更新是从父进程发送的。同样,父进程也会定期收到来自计算进程的新结果的通知。请注意,连接器的
recv()
方法被阻塞,即如果另一端还没有发送任何东西,recv()
将等到有东西可以阅读,并防止在此期间发生其他任何事情。所以请注意这一点。希望这可以帮助。同样,这是一个准系统示例,在现实生活中你会想要做更多的错误处理,可能使用
poll()
关于连接对象等等,但希望这能传达主要想法并帮助您入门。
关于python - 如何在 Python 中控制模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16020242/