本文是由翻译组最新翻译制作而成的, 原作者是,译者是,并且是经过编程派名为的作者进行校对的。昨天, 编程派发布了一篇多线程编程的教程, 通过这篇教程能够让人学会多线程编程。
译者简介: 其为成都信息工程大学的学生, 且是计算机学院的。擅长的领域是图像处理以及机器学习。
多进程()模块于2.6版本被加入, 它最初由Jesse以及在PEP 371中予以定义, 该模块生成进程的方式与你运用模块生成线程的方式相同, 然而在此处, 因你所使用的是多进程, 故你能够规避全局解释锁(GIL), 进而充分借助机器多处理器的优点。
模块之中存在着一部分, 是模块所不具备的某些 API。比如说, 存在一个构思精巧的 Pool 类, 它能够使你借助多个输入, 以并行的方式去执行一个函数。我们会在后续的章节里遇上 Pool。我们会从模块的类着手展开介绍。
入门
类跟模块的类极为相像。咱们着手去构建一连串调用同一函数的进程, 瞧瞧它将会以怎样的状况运行:

在这个例子当中, 我们进行了导入操作 , 接着创建了一个 的函数。在所述函数里, 我们把传入的数字予以扩大两倍。我们进一步运用 中的 os 这项工具得到当前进程的ID(换句话说是 pid)。这能够告知我们哪一个进程正处于调用此函数的状态。在代码底部的那个循环之内, 我们创建了彼此成系列的进程并启动了它们。最下方的那个循环在每一项进程上调用了join() 这个方法, 它会告知 等待着某进程结尾才停止。要是你有促使某个进程最终宣告完成目标行为以便彻底停止的需求, 你能够调用它的 () 这个方法。
当运行这段代码的时候,你将看到类似下边这样的输出:
5 to 10 by id: 10468
10 to 20 by id: 10469
15 to 30 by id: 10470
20 to 40 by id: 10471
25 to 50 by id: 10472
有时候, 使进程具备一个具备可读性的名字会更具优势。幸运的是, 类能够支持为进程进行命名。让我们来瞧一瞧:

这一回, 我们引进了些许别样事物。 跟 模块里的大致相同。我们借助它去获取正调用 函数的线程的名称。你或许留意到了, 我们前面的五个进程并未设置名称。第六个进程我们赋予其名为"Test", 我们瞧瞧所获得的输出。
5 to 10 by: -2
10 to 20 by: -3
15 to 30 by: -4
20 to 40 by: -5
25 to 50 by: -6
2 to 4 by: Test
输出呈现出这样一定的情况是, 模块依照默认的条件, 为每一个进程的名字,去指派了一个数字。当然了, 我们进行设置名字操作的那个进程, 是不存在数字的。
模块如同这一模块那般相类, 同样对"锁"予以支持, 你所要从事的则是进行锁定, 将其予以获取, 在履行操作 再把其进行释放, 让我们来瞧一瞧:

我们在此处创建了一个函数, 该函数会直接打印传递过来的任意事物。为避免线程受到其他事务的干扰, 我们使用了一个 Lock 对象。这段代码会对我们列表里的三项内容进行遍历, 并且会分别为其创建一个进程。每个进程都会调用这个函数, 还会把可迭代对象中的一个元素传入该函数。由于我们使用了锁, 所以后续的进程将会等待锁被释放之后, 才会继续执行。
日志
多进程所产生的日志跟啊多线程所产生的日志存在着那么一点区别。之所以会这样, 缘由在于啊那个特定的包它并不对进程共享锁予以支持哇所以呢由不同进程而来的些日志啊它就极有可能混杂到一块儿去。咱们试着去做一下哈在上边提及呀例子当中添加一个基础日志。下面呈现的便是代码!

以最简单的方式记录日志, 是把所有日志发送给。完成这般操作通过调用函数 来达成。接着调用 函数获取记录器(), 并将日志级别设定为 INFO。代码的其余部分与原先一致。需要着重表明的是, 我未曾运用 join() 函数。与之形成对照的是, 父线程在退出之际要特地调用 join()。
当你运行上边的代码,会得到像下面这样的输出:
INFO/-1
child self.run()
tango
INFO/-1
down
INFO/-1
with 0
INFO/-2
child self.run()
INFO/
down
INFO/-2
down
INFO/-3
child self.run()
INFO/-2
with 0
10
INFO/
join() for -3
INFO/-3
down
INFO/-3
with 0
INFO/
join() for -2
倘若当下你打算将日志留存至硬盘, 实际上是存在一定复杂性的, 你能够参照相关官方实例。
Pool 类
用于表示一个工作进程的池子的是Pool类, 它存在一些方法, 这些方法能够让你把任务分配给不同的工作进程我们看一下关于这个的简单例子:

于此处创建了个Pool的实例, 然后告知其去创建三个工作进程。之后运用map方法将一函数以及一可迭代对象映射于每个进程。最终把呈现于此的结果打印出来, 此结果为一个列表, 有此结果:
10, 20, 40
我们同样可以通过 方法得到程序的结果:

这么做能够使我们获取进程结果, get函数所具备的功能便是如此。你或许会留意到, 我们增添了一个超时()的设定, 这旨在避免我们所调用的函数出现意外状况。我们不愿让它被无限期地予以阻塞。
进程通信
要是打算促使两个不同的进程去进行通讯交流的话, 相关模块为此提供了两个主要的办法, 其一为Pipes, 其二为Queue且Queue确保了线程以及进程的安全性, 在另一篇专门针对线程的文章里头也含有一个Queue的实现方式, 我们乃是基于此而做出一些相应的改动:

我们须导入Queue和, 接着我们去创建两个函数, 其一创建数据并将它们增添至队列里, 其二取出数据并对它们予以处理。把数据添加进队列所选的是Queue的put( )方法, 取出数据选取的是get( )方法。最后一段代码构建了一个队列对象及一些进程, 并运行这些。要留意, 我们是于进程对象上调用连接的join( )方法而非在Queue自身之中。
结束语
本文讲述了诸多内容, 你已然学习了怎样对函数运用多进程, 借助队列达成进程间的通信, 为线程命名等等。在的官方文档当中尚有许多内容在本文内未被涉及到, 你也能够去更深入地钻研一番。然而如今你已经学会如何经由利用你计算机的全部处理性能啦。