Python: How can I run python functions in parallel?
我先研究了一下,却找不到答案。 我试图在Python中并行运行多个函数。
我有这样的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | files.py import common #common is a util class that handles all the IO stuff dir1 = 'C:\folder1' dir2 = 'C:\folder2' filename = 'test.txt' addFiles = [25, 5, 15, 35, 45, 25, 5, 15, 35, 45] def func1(): c = common.Common() for i in range(len(addFiles)): c.createFiles(addFiles[i], filename, dir1) c.getFiles(dir1) time.sleep(10) c.removeFiles(addFiles[i], dir1) c.getFiles(dir1) def func2(): c = common.Common() for i in range(len(addFiles)): c.createFiles(addFiles[i], filename, dir2) c.getFiles(dir2) time.sleep(10) c.removeFiles(addFiles[i], dir2) c.getFiles(dir2) |
我想调用func1和func2并使它们同时运行。 这些功能彼此之间或在同一对象上不相互作用。 现在,我必须等待func1完成才能启动func2。 我该如何执行以下操作:
1 2 3 4 5 | process.py from files import func1, func2 runBothFunc(func1(), func2()) |
我希望能够几乎同时创建两个目录,因为我每分钟都在统计要创建多少个文件。 如果该目录不存在,它将使我的时间安排变慢。
您可以使用
由于CPython的特殊性,
这是一个完整的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from multiprocessing import Process def func1(): print 'func1: starting' for i in xrange(10000000): pass print 'func1: finishing' def func2(): print 'func2: starting' for i in xrange(10000000): pass print 'func2: finishing' if __name__ == '__main__': p1 = Process(target=func1) p1.start() p2 = Process(target=func2) p2.start() p1.join() p2.join() |
启动/联接子进程的机制可以很容易地按照
1 2 3 4 5 6 7 8 9 10 | def runInParallel(*fns): proc = [] for fn in fns: p = Process(target=fn) p.start() proc.append(p) for p in proc: p.join() runInParallel(func1, func2) |
可以使用Ray优雅地完成此任务,该系统使您可以轻松地并行化和分发Python代码。
要并行化示例,您需要使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import ray ray.init() dir1 = 'C:\\folder1' dir2 = 'C:\\folder2' filename = 'test.txt' addFiles = [25, 5, 15, 35, 45, 25, 5, 15, 35, 45] # Define the functions. # You need to pass every global variable used by the function as an argument. # This is needed because each remote function runs in a different process, # and thus it does not have access to the global variables defined in # the current process. @ray.remote def func1(filename, addFiles, dir): # func1() code here... @ray.remote def func2(filename, addFiles, dir): # func2() code here... # Start two tasks in the background and wait for them to finish. ray.get([func1.remote(filename, addFiles, dir1), func2.remote(filename, addFiles, dir2)]) |
如果将相同的参数传递给两个函数且参数较大,则更有效的方法是使用
1 2 3 | largeData_id = ray.put(largeData) ray.get([func1(largeData_id), func2(largeData_id)]) |
如果
1 2 3 | ret_id1 = func1.remote(filename, addFiles, dir1) ret_id2 = func1.remote(filename, addFiles, dir2) ret1, ret2 = ray.get([ret_id1, ret_id2]) |
与多处理模块相比,使用Ray有许多优点。特别是,相同的代码将在单台计算机以及一台计算机集群上运行。有关Ray的更多优点,请参见此相关文章。
如果您是Windows用户并且使用python 3,那么本文将帮助您在python中进行并行编程。当您运行常规的多处理库的池编程时,您将在程序中遇到关于主要功能的错误。这是因为Windows没有fork()功能。下面的帖子提供了上述问题的解决方案。
http://python.6.x6.nabble.com/Multiprocessing-Pool-woes-td5047050.html
由于我使用的是python 3,因此我对该程序进行了如下更改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | from types import FunctionType import marshal def _applicable(*args, **kwargs): name = kwargs['__pw_name'] code = marshal.loads(kwargs['__pw_code']) gbls = globals() #gbls = marshal.loads(kwargs['__pw_gbls']) defs = marshal.loads(kwargs['__pw_defs']) clsr = marshal.loads(kwargs['__pw_clsr']) fdct = marshal.loads(kwargs['__pw_fdct']) func = FunctionType(code, gbls, name, defs, clsr) func.fdct = fdct del kwargs['__pw_name'] del kwargs['__pw_code'] del kwargs['__pw_defs'] del kwargs['__pw_clsr'] del kwargs['__pw_fdct'] return func(*args, **kwargs) def make_applicable(f, *args, **kwargs): if not isinstance(f, FunctionType): raise ValueError('argument must be a function') kwargs['__pw_name'] = f.__name__ # edited kwargs['__pw_code'] = marshal.dumps(f.__code__) # edited kwargs['__pw_defs'] = marshal.dumps(f.__defaults__) # edited kwargs['__pw_clsr'] = marshal.dumps(f.__closure__) # edited kwargs['__pw_fdct'] = marshal.dumps(f.__dict__) # edited return _applicable, args, kwargs def _mappable(x): x,name,code,defs,clsr,fdct = x code = marshal.loads(code) gbls = globals() #gbls = marshal.loads(gbls) defs = marshal.loads(defs) clsr = marshal.loads(clsr) fdct = marshal.loads(fdct) func = FunctionType(code, gbls, name, defs, clsr) func.fdct = fdct return func(x) def make_mappable(f, iterable): if not isinstance(f, FunctionType): raise ValueError('argument must be a function') name = f.__name__ # edited code = marshal.dumps(f.__code__) # edited defs = marshal.dumps(f.__defaults__) # edited clsr = marshal.dumps(f.__closure__) # edited fdct = marshal.dumps(f.__dict__) # edited return _mappable, ((i,name,code,defs,clsr,fdct) for i in iterable) |
使用此功能后,上面的问题代码也做了如下更改:
1 2 3 4 5 6 7 8 9 10 | from multiprocessing import Pool from poolable import make_applicable, make_mappable def cube(x): return x**3 if __name__ =="__main__": pool = Pool(processes=2) results = [pool.apply_async(*make_applicable(cube,x)) for x in range(1,7)] print([result.get(timeout=10) for result in results]) |
我得到的输出为:
1 | [1, 8, 27, 64, 125, 216] |
我认为这篇文章可能对某些Windows用户有用。
无法保证两个函数将彼此同步执行,这似乎是您想要执行的操作。
最好的办法是将函数分成几个步骤,然后使用
这比
如果您的函数主要用于执行I / O工作(而减少了CPU工作),并且您拥有Python 3.2+,则可以使用ThreadPoolExecutor:
1 2 3 4 5 6 7 8 9 10 11 12 | from concurrent.futures import ThreadPoolExecutor def run_io_tasks_in_parallel(tasks): with ThreadPoolExecutor() as executor: running_tasks = [executor.submit(task) for task in tasks] for running_task in running_tasks: running_task.result() run_io_tasks_in_parallel([ lambda: print('IO task 1 running!'), lambda: print('IO task 2 running!'), ]) |
如果您的功能主要是在做CPU工作(而I / O则更少),并且您拥有Python 2.6+,则可以使用多处理模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 | from multiprocessing import Process def run_cpu_tasks_in_parallel(tasks): running_tasks = [Process(target=task) for task in tasks] for running_task in running_tasks: running_task.start() for running_task in running_tasks: running_task.join() run_cpu_tasks_in_parallel([ lambda: print('CPU task 1 running!'), lambda: print('CPU task 2 running!'), ]) |