不使用线程或编写单独的文件/脚本,能否在子进程中运行函数?

103
import subprocess

def my_function(x):
    return x + 100

output = subprocess.Popen(my_function, 1) #I would like to pass the function object and its arguments
print output 
#desired output: 101

我只找到了使用单独脚本打开子进程的文档。有人知道如何传递函数对象或者甚至是传递函数代码的简便方法吗?


1
我相信你正在寻找multiprocessing模块。 - Noctis Skytower
4个回答

141

我认为你更需要的是类似于multiprocessing模块的东西:

http://docs.python.org/library/multiprocessing.html#the-process-class

subprocess模块用于生成进程并处理它们的输入/输出 - 不适用于运行函数。

以下是您的代码的版本:

from multiprocessing import Process, Queue

# must be a global function    
def my_function(q, x):
    q.put(x + 100)

if __name__ == '__main__':
    queue = Queue()
    p = Process(target=my_function, args=(queue, 1))
    p.start()
    p.join() # this blocks until the process terminates
    result = queue.get()
    print result

25
您可以使用processify装饰器作为快捷方式:https://gist.github.com/2311116 - schlamar
3
我假设这将克隆Python解释器及其环境,以供子进程使用? - Jens
2
这是一个基于 processify 的分支,适用于 Python 3 并支持生成器函数。https://gist.github.com/stuaxo/889db016e51264581b50 - Stuart Axon
8
请注意,如果您通过队列传递非常大的数据,则此代码可能会发生死锁 - 在加入进程之前始终使用queue.get()方法获取数据,否则它将尝试写入队列而没有任何读取该队列的操作,从而导致程序挂起。 - Petr Baudis
@schlamar 我想在后台运行一个函数,但我有一些资源限制,不能无限次地运行该函数,因此想要将额外的函数执行排队。你有任何想法吗?我在这里提出了我的问题(https://stackoverflow.com/questions/49081260/executing-a-function-in-the-background-while-using-limited-number-of-cores-threa)。你能否请看一下我的问题?任何帮助都将不胜感激! - Amir
如果我不需要结果,可以不传递队列吗?同时,当my_function在处理过程中时,它能否调用另一个函数? - 3awny

24

你可以使用标准的Unix fork系统调用,例如os.fork()fork()将创建一个新进程,并运行同样的脚本。在新进程中,它将返回0,而在旧进程中它将返回新进程的进程ID。

child_pid = os.fork()
if child_pid == 0:
  print "New proc"
else:
  print "Old proc"

如果您需要一个提供多进程支持的高级库,并提供使用多个进程的可移植抽象,那么可以考虑使用multiprocessing模块。IBM DeveloperWorks上有一篇文章Python多进程编程,介绍了这两种技术。


我很好奇,为什么要点踩?我的回答有什么问题吗? - Brian Campbell
多进程不仅是对fork()的更高级封装,而且它是一个跨平台的多进程工具包(在unix上使用fork)。这很重要,因为这意味着它可以在Windows等操作系统上运行,而fork()则不能。编辑:这就是投票反对的原因,尽管后来我觉得可能没必要。不过现在已经晚了,无法撤回。编辑2:更确切地说,当不支持跨平台时建议使用fork()是被反对的原因。 - Devin Jeanpierre
4
@Devin,如果你想的话,你总是可以撤回自己投的反对票。 - Alex Martelli
那么我来澄清一下。我明确提到了fork不可移植性的问题;通常我会给出非可移植性的答案,并提供它们是非可移植的信息,让提问者自行决定是否足够满意。由于我已经编辑了我的回答,如果您认为我已经做出了足够的改进,您应该可以取消您的负评;如果您没有这样做,也没有关系,我只是想检查一下我哪里做错了。 - Brian Campbell
@Alex,不行,你不能这样做。一旦时间过去了一段时间,除非进行编辑,否则你就无法撤回它。在我重新考虑之前,已经过了这么长时间,因此才有了“太晚”的评论。无论如何,正如我所说,我已经决定不值得了,所以它已经消失了。我也很感激并理解你的原因,我很高兴无论如何都不会有什么难过的感觉。:p - Devin Jeanpierre
@BrianCampbell 我想在后台运行一个函数,但我有一些资源限制,不能无限次运行该函数,因此想将额外的函数执行排队。你有任何想法吗?我在这里提出了我的问题(https://stackoverflow.com/questions/49081260/executing-a-function-in-the-background-while-using-limited-number-of-cores-threa)。你能否看一下我的问题,并看看是否可以给我一些提示(或者更好的是,一个答案)关于我应该如何做到这一点? - Amir

9
import threading
import time

def blocker():
    while True:
        print "Oh, sorry, am I in the way?"
        time.sleep(1)

t = threading.Thread(name='child procs', target=blocker)
t.start()

# Prove that we passed through the blocking call
print "No, that's okay" 

您可以使用setDaemon(True)功能立即将线程置于后台。

4
请注意,由于全局解释器锁(GIL),在Python中使用线程只对等待任务(即非CPU绑定任务)有用。对于CPU绑定任务,必须使用多进程技术。 - c z

6
您可以使用concurrent.futures.ProcessPoolExecutor,它不仅传播返回值,还会传播任何异常。
import concurrent.futures

# must be a global function    
def my_function(x):
    if x < 0:
        raise ValueError
    return x + 100

with concurrent.futures.ProcessPoolExecutor() as executor:
    f = executor.submit(my_function, 1)
    ret = f.result()  # will rethrow any exceptions

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接