最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

python基础教程之 快速学会多进程

XAMPP案例 admin 93浏览 0评论

drr00067

python 多进程

快速学会多进程

众所周知,python里的多线程是假的。

所以,当需要缩短运行时间,又不想费心思优化代码时,多进程就必须要上了。反正服务器这么多cpu,不用白不用嘛!

有一个耗时很长的任务。

import time
def do_something(x):
    time.sleep(3)

我们需要它在不同的输入下运行多次。

正常的写法是这样的。

%%time
for i in range(5):
    do_something(i)

out:

CPU times: user 13.2 ms, sys: 2.36 ms, total: 15.6 ms
Wall time: 15 s

显然,这需要15s(废话)。

这种就叫串行。

多进程就是同时开启多个进程,占据多个cpu,并行处理,榨干算力(不是)。

下面,六行代码将上述任务改成多进程运行。

首先导入库,然后定义一个进程池。

import multiprocessing as mp
pool = mp.Pool()

将每一次的函数运行都打包成一个进程,放入进程池,使用apply_async函数。

for i in range(5):
    pool.apply_async(do_something, args = (i,))

apply_async的第一个参数是函数,args参数是函数的形参,每次给不同参数。

然后关闭进程池,运行。具体含义没必要懂,反正加上这两行就对了。

pool.close()
pool.join()

合起来试下。

%%time
import multiprocessing as mp
pool = mp.Pool()
for i in range(5):
    pool.apply_async(do_something, args = (i,))
pool.close()
pool.join()

out:

CPU times: user 10.9 ms, sys: 52.5 ms, total: 63.4 ms
Wall time: 3.08 s

哦吼,3.08 s,基本上就是一次运行的时间。

但是只运行肯定不是我们的目标,更常用的操作是将每一次运行产生的结果汇总起来。

def do_something(x,y):
    time.sleep(3)
    return x*y

当进程函数有返回值时,值会存储在生成的进程对象中。

%%time
pool = mp.Pool()
processes = []
for i in range(5):
    process = pool.apply_async(do_something, args=(i,), kwds={'y': i + 1})
    processes.append(process)
pool.close()
pool.join()
results = [i.get() for i in processes]
print(results)

out:

[0, 2, 6, 12, 20]
CPU times: user 11 ms, sys: 55.8 ms, total: 66.8 ms
Wall time: 3.08 s

进程存入列表,然后对每个进程使用get(),获得函数返回值。

这里我还使用了kwds参数,用于提供函数的实参。

还有一种方式是使用callback参数。

%%time
results = []

def store(x):
    results.append(x)

pool = mp.Pool()
processes = []
for i in range(5):
    pool.apply_async(do_something, args=(i,), kwds={'y': i + 1}, callback=store)
pool.close()
pool.join()
print(results)

out:

[0, 2, 12, 20, 6]
CPU times: user 14.4 ms, sys: 58.3 ms, total: 72.8 ms
Wall time: 3.09 s

callback参数提供的函数会在进程结束后运行,默认参数即为进程函数的返回值。可以利用它完成后续的处理工作。

是不是跃跃欲试了呢,别慌,看一下注意事项。

注意事项

One

在windows下使用python多进程时,进程代码必须包含在main代码块里,像下面这样,而mac和linux无此限制。

if __name__ == "__main__":
    pool = mp.Pool()
    for i in range(5):
        pool.apply_async(do_something, args = (i,))
    pool.close()
    pool.join()

别问,问我也不知道为什么。

Two

第二个重要的点是,谨慎对待进程函数与外界资源的交互。

当然,读肯定是没问题的。

%%time
nums = [5, 4, 3, 2, 1]

def do_something(x):
    time.sleep(2)
    return x * nums[x]

pool = mp.Pool()
results = []
for i in range(5):
    process = pool.apply_async(do_something, args=(i,))
    results.append(process)
pool.close()
pool.join()
results = [i.get() for i in results]
print(results)

out:

[0, 4, 6, 6, 4]
CPU times: user 8.55 ms, sys: 60.6 ms, total: 69.1 ms
Wall time: 3.08 s

如果同时修改呢?

想一想也知道,不可能啊,几个进程是同时运行的,你也改,我也改,那最后不就乱套了吗?

%%time
nums = [5, 4, 3, 2, 1]

def do_something(x):
    time.sleep(3)
    nums.append(x)
    return nums[-1]

pool = mp.Pool()
results = []
for i in range(5):
    process = pool.apply_async(do_something, args=(i,))
    results.append(process)
pool.close()
pool.join()
results = [i.get() for i in results]
print(results)
print(nums)

out:

[0, 1, 2, 3, 4]
[5, 4, 3, 2, 1]
CPU times: user 12.6 ms, sys: 50.8 ms, total: 63.4 ms
Wall time: 3.08 s

results的结果,以为完成了改动,但是修改的其实是另一个进程上的nums,实际上的nums还和原来一样。

但是,(认真脸),如果我必须要同时修改,就是有这么个需求,怎么办?

确实有办法。

%%time
manager = mp.Manager()
nums = manager.list([5, 4, 3, 2, 1])

def do_something(x):
    time.sleep(3)
    nums.append(x)
    return nums[-1]

pool = mp.Pool()
results = []
for i in range(5):
    process = pool.apply_async(do_something, args=(i,))
    results.append(process)
pool.close()
pool.join()
results = [i.get() for i in results]
print(results)
print(nums)

mp.Manager能够生成一个单独的服务进程,此处,使用它的list()生成一个列表,该列表可以被所有其他进程修改。

列表的使用和正常列表基本没有差别。

out:

[0, 1, 2, 3, 2]
[5, 4, 3, 2, 1, 3, 4, 2, 0, 1]
CPU times: user 14.7 ms, sys: 60.7 ms, total: 75.5 ms
Wall time: 3.22 s

显然,结果中的nums改变了。同时,results的结果也不受控制了,因为我们永远不可能知道,在读取列表最后一个值之前,有没有其他进程刚刚修改了该列表。

而且能够注意到,总体时间比之前多了0.2s,这一部分就是生成服务进程的开销。

Manager对象也包含dict以及其他常用数据结构,想必可以满足不同需要了。

END

python多进程的实现方式有许多种,其实会一种就够了,以不变应万变。

转载请注明:XAMPP中文组官网 » python基础教程之 快速学会多进程