多进程multiprocessing模块

介绍

multiprocessing 是一个用于产生进程的包,具有与 threading 模块相似API。 multiprocessing 包同时提供本地和远程并发,使用子进程代替线程,有效避免 Global Interpreter Lock 带来的影响。因此, multiprocessing 模块允许程序员充分利用机器上的多核。可运行于 Unix 和 Windows 。

  • Unix/Linux实现多进程

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前父进程复制了一份子进程,然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。这样,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:

  • Windows的多进程
    由于Windows没有fork调用,而如果我们需要在Windows上用Python编写多进程的程序,就需要使用到multiprocessing模块。由于Python是跨平台的,它提供一个跨平台的多进程支持,multiprocessing模块就是跨平台版本的多进程模块。

注意:子进程执行是没有顺序的,先执行哪个子进程操作系统说了算的。而且进程的创建和销毁也是非常消耗资源的,所以如果进行一些本来就不需要多少耗时的任务你会发现多进程甚至比单进程还要慢
上述操作系统的进程知识介绍参考:Python3多进程multiprocessing模块的使用
廖雪峰–多进程
multiprocessing类中,通过cpu_count()方法和active_children()方法,获取当前机器的cpu核心数量,以及得到目前所有的运行的进程。
multiprocessing中的部分方法具体介绍:

cpu_count()

返回系统的CPU数量。该数量不同于当前进程可以使用的CPU数量。

active_children()

返回当前进程存活的子进程的列表。
调用该方法有“等待”已经结束的进程的副作用。

current_process()

返回与当前进程相对应的 Process 对象。

parent_process()

返回父进程 Process 对象,和父进程调用 current_process() 返回的对象一样。如果一个进程已经是主进程, parent_process 会返回 None.

freeze_support()

为使用了 multiprocessing 的程序,提供冻结以产生 Windows 可执行文件的支持。(在 py2exe, PyInstaller 和 cx_Freeze 上测试通过)

需要在 main 模块的 if name == ‘main’ 该行之后马上调用该函数。
如果没有调用 freeze_support() 在尝试运行被冻结的可执行文件时会抛出 RuntimeError 异常。

对 freeze_support() 的调用在非 Windows 平台上是无效的。如果该模块在 Windows 平台的 Python 解释器中正常运行 (该程序没有被冻结), 调用freeze_support() 也是无效的。
(这个方法看不懂[捂脸])

pool进程池

class multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])

如果要启用大量的子进程,可以使用进程池的方式,也就是Pool模块。Pool为一个进程池对象,它控制可以提交作业的工作进程池。它支持带有超时和回调的异步结果,以及一个并行的 map 实现。需要注意的是:multiprocessing.pool 对象具有需要正确管理的内部资源 (像任何其他资源一样),具体方式是将进程池用作上下文管理器,或者手动调用 close() 和 terminate()。 未做此类操作将导致进程在终结阶段挂起。请注意依赖垃圾回收器来销毁进程池是 不正确 的做法,因为 CPython 并不保证会调用进程池终结程序
进程池对象初始化参数有processes,processes 是要使用的工作进程数目。如果 processes 为 None,则使用 os.cpu_count() 返回的值。
一个简单的官方例子:

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    with Pool(5) as p:
        print(p.map(f, [1, 2, 3]))
#输出为:[1, 4, 9]

为什么使用if _name_==’_main_:确保主模块可以被新启动的Python解释器安全导入而不会引发什么副作用(比如又启动了一个子进程)应该使用 if __name__ == ‘__main__’: ,从而保护程序"入口点":

下面是pool对象的方法介绍:

map(func, iterable[, chunksize])

这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。其中map()函数作用是,返回一个将 function 应用于 iterable 中每一项并输出其结果的迭代器。 如果传入了额外的 iterable 参数,function 必须接受相同个数的实参并被应用于从所有可迭代对象中并行获取的项。 当有多个可迭代对象时,最短的可迭代对象耗尽则整个迭代就将结束。
map的用法有很多:

  • 当iterable为多个时,map并行执行每个过程
list(map(lambda x , y : x ** y, [2,4,6],[3,2,1]))
Out[17]: [8, 16, 6]
  • 使用map()函数可以实现数据类型转换
list(map(int, (1,2)))
Out[13]: [1, 2]
list(map(int, '1234'))
Out[14]: [1, 2, 3, 4]
list(map(tuple, '1234'))
Out[15]: [('1',), ('2',), ('3',), ('4',)]

注意对于很长的迭代对象,可能消耗很多内存。可以考虑使用 imap() 或 imap_unordered() 并且显示指定 chunksize 以提升效率。

imap(func, iterable[, chunksize])

是map() 的延迟执行版本,而pool类中的map()方法又是内置map()函数的并行版本。

imap_unordered(func, iterable[, chunksize])

和 imap() 相同,只不过通过迭代器返回的结果是任意的。(当进程池中只有一个工作进程的时候,返回结果的顺序才能认为是"有序"的)

close()

阻止后续任务提交到进程池,当所有任务执行完成后,工作进程会退出。

terminate()

不必等待未完成的任务,立即停止工作进程。当进程池对象被垃圾回收时, 会立即调用 terminate() 。
避免杀死进程。 Process.terminate 停止一个进程很容易导致这个进程正在使用的共享资源(如锁、信号量、管道和队列)损坏或者变得不可用,无法在其他进程中继续使用。

所以,最好只对那些从来不使用共享资源的进程调用 Process.terminate 。

join()

使用 Join 避免僵尸进程,等待工作进程结束。调用 join() 前必须先调用 close() 或者 terminate() 。在 Unix 上,如果一个进程执行完成但是没有被 join,就会变成僵尸进程。一般来说,僵尸进程不会很多,因为每次新启动进程(或者 active_children() 被调用)时,所有已执行完成且没有被 join 的进程都会自动被 join,而且对一个执行完的进程调用 Process.is_alive 也会 join 这个进程。尽管如此,对自己启动的进程显式调用 join 依然是最佳实践。当Pool所有的进程任务完成后,会产生僵尸进程,如果主线程不结束,系统不会自动回收资源,需要调用join函数去回收。

下面是进程池使用的一个例子:

def f(x):
    return x*x
pool=Pool(processes=4)
pool.map(f,range(4))
Out[32]: [0, 1, 4, 9]
it = pool.imap(f, range(10))
it.next()
Out[34]: 0
next(it)
Out[35]: 1
pool.close()

进程间通信

进程间通信
Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。

(待学习…)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值