首页 / 资讯中心 / 文章详情

Python多进程编程实战指南

Python多进程编程实战指南 ★ FEATURED ARTICLE
这份教程是关于使用多进程的方法来进行程序开发的。在这篇文章里, 咱们要去学一学是怎么去通过那个东西来来达成多进程编程这个样子的一个操作的, 同时呢, 对于这个里头所涉及到的那种非常先进的这些概念, 咱们也将要去把它给好好讨论一番。到底什么叫做多进程编写程序?所谓的多进程, 它其实是系统在某种能力上面的一种体现, 而这种体现就是系统能够并行地来运行一个或者说是多个进程。我们可以说简单一点, 就是在这个多进程里面, 它是利用单台的那一台计算机系统内部的两个或者是更多的个CPU来进行配合的。同时呢, 这个方法它还具备能够在不同的进程之间进行任务的分配的功能。在处理单元与外部设备, 还有主内存之间建立起共享关系之后, 程序便能够被同时进行处理。对于多进程应用程序这种类型的东西, 它们会先被分割成更细小的组成部分, 然后各自独立地去进行运行。至于每一个进程具体该由处理器中的哪一个来负责处理的情况, 则是由操作系统来进行分配的。这里提供了一个内置模块, 支持进程切换。在使用多进程之前, 我们必须熟悉进程对象。那么, 究竟是为了什么原因才需要去实行多进程这种编程方式的呢。多进程编程对于在计算机系统内部执行多个任务来说, 是非常非常重要的。如果我们去假设某一个计算机, 它根本就没有拥有多进程的这个功能, 或者是它只有一个简单的处理器。在这种情况下我们同时将许多不同的进程分配给这个电脑系统。然后, 它只能中断那个正在进行的任务, 接着去干别的活, 好让其他程序能够正常运转, 这情形就跟一位厨师自己在厨房里单干差不多, 他需要兼顾好多事情, 包括切菜、清扫现场、做饭、和面以及烘烤之类的流程, 以便最终弄出一道吃的东西。因此, 多进程编程对于在同一时间执行多个任务而无需中断显得至关重要了。它还可以轻松跟踪所有的这些任务的状况。这就是为什么多进程编程的概念会产生出来的根本原因。在多进程编程这一过程里面, CPU可以往里头分配多个任务, 然后每个任务啊都拥有属于自己的处理器。中的多进程编程该系统提供了一个模块, 这个模块是用来在单个系统内部去执行多个不同的任务的。它还提供了一套用户界面非常友好而且直观的操作接口, 这套操作接口主要是用来处理多进程编程相关工作的。我们先一起来对多进程编程的这个简单示例进行一个理解和认识吧。示例 -from multiprocessing import Processdef disp():print (Hello !!Welcome to Python Tutorial)if __name__ __main__:p Process(targetdisp)p.start()p.join()输出Hello !! Welcome to Python Tutorial说明在上面那段代码中间, 咱们先把那个对应的类给导入进来, 紧接着就在 disp 这个函数里面去弄出来一个相关的操作对象, 后面咱们调用 start 方法来让这个进程跑起来, 再用 join 这个方法把进程结束这件事儿给处理妥当, 除此之外咱还能利用 args 这个关键字参数, 在已经写好声明好的函数里去传递各种数据进去。我们接下来要把这个包含了参数的多进程编程方面的例子进行一番解读, 进而让我们能够明白它是怎么一回事。这个例子是编号为二的示例内容。# Python multiprocessing example# importing the multiprocessing moduleimport multiprocessingdef cube(n):# This function will print the cube of the given numberprint(The Cube is: {}.format(n * n * n))def square(n):# This function will print the square of the given numberprint(The Square is: {}.format(n * n))if __name__ __main__:# creating two processesprocess1 multiprocessing.Process(target square, args(5, ))process2 multiprocessing.Process(target cube, args(5, ))# Here we start the process 1process1.start()# Here we start process 2process2.start()# The join() method is used to wait for process 1 to completeprocess1.join()# It is used to wait for process 1 to completeprocess2.join()# Print if both processes are completedprint(Both processes are finished)输出The Cube is: 125The Square is: 25Both processes are finished说明在这个上面的例子里面, 我们建立起了函数 cube() 这个函数是用来计算给定的数字的立方, 然后还有一个 () 这个函数是用来计算给定的数字的平方。紧接着, 我们对 这个类创建了一个进程对象。在该对象初始化过程中, 需要传入两个参数。第一个参数用于指定需要被调用的具体函数。第二个参数的名称固定为 args。此参数的主要用途是给上述指定的函数传递对应的入口参数。process1 multiprocessing.Process(target square, args(5, ))process2 multiprocessing.Process(target cube, args(5, ))我们采用start( )这种方法, 来把进程进行启动。process1.start()process2.start()就像大家在输出文件里所看到的那样, 该程序首先要等待第一个进程结束, 接着再等待第二个进程完毕。等到这两个进程全部执行完成之后, 才会去执行最后的那一句代码。中的多进程编程类该模块提供了用于构建并行程序的大量常用类。下文将对核心的类进行具体介绍, 重点包含Queue 类以及 Lock 类。有关特定类的细节信息, 在前文示例中已经有所涉及。接下来的内容将把分析重心转向对 Queue 和 Lock 这两个类本身的探讨。让我们来查看一个, 这个简单的示例能够获取当前在系统之中处于工作状态状态下的CPU的数量。示例 -import multiprocessingprint(The number of CPU currently working in system : , multiprocessing.cpu_count())输出(The number of CPU currently woking in system : , 32)上面说到的那种处理器的数量情况, 可能会因为在不同的计算机设备上面而有所不同。在咱们这儿的计算机配置里, 核心的具体数目是被设定成了叁拾贰这个数值。使用进程队列类来进行多进程编程操作。大家都晓得队列这个东西在数据结构领域算是特别重要的那一块儿, 它的这个结构, 是和数据结构里的队列完全一模一样的, 就是基于所谓的那个“先进先出”这么一个概念来搞的, 一般来说, 队列里头经常是存的那些对象, 而且在进程和进程之间要是在进行数据共享的时候它也就扮演着一个很重要的角色。队列是被传递到目标函数的里面的参数, 这么做的目的是, 为了允许进程去进行数据的消耗。队列会提供 put() 这个函数来插入数据, 也会提供 get() 这个函数来从队列那里获取数据。让我们来理解下面展示的例子。示例 -# Importing Queue Classfrom multiprocessing import Queuefruits [Apple, Orange, Guava, Papaya, Banana]count 1# creating a queue objectqueue Queue()print(pushing items to the queue:)for fr in fruits:print(item no: , count, , fr)queue.put(fr)count 1print(\npopping items from the queue:)count 0while not queue.empty():print(item no: , count, , queue.get())count 1输出pushing items to the queue:(item no: , 1, , Apple)(item no: , 2, , Orange)(item no: , 3, , Guava)(item no: , 4, , Papaya)(item no: , 5, , Banana)popping items from the queue:(item no: , 0, , Apple)(item no: , 1, , Orange)(item no: , 2, , Guava)(item no: , 3, , Papaya)(item no: , 4, , Banana)说明在上述的代码里面, 我们导入了 Queue 类, 还初始化了一个叫空的列表。接下来, 我们把赋值给计数的那个数字设为 1。计数的变量是用来计算元素的总共有多少个的。然后呢, 我们通过去调用 Queue 这个方法创建出来一个队列的对象, 这个对象主要是用来在队列里去执行操作的。在 for 循环中, 我们通过使用 put() 函数来将元素逐个插入到队列里, 并且会在每一次循环的迭代过程之中把计数的数值增加一个1。在进行多进程编程的时候, 会使用到进程锁这类工具。锁类这个工具, 是被用来在处理进程的时候去拿到一把锁头的, 这么做的目的是为了拦住别的进程, 不让它们去干那些差不多的代码活儿, 除非直到这把锁头最终被松绑才行。这个锁类, 主要是为了把两大件事给办了。头一件事儿呢, 是要用到那个叫 () 的函数, 好让锁被拿在手里头。第二件事儿咧, 就是要用到那个也叫 () 的函数, 目的就是把锁头给松开、给释放掉。这里为大家提供了一个关于多进程编程的具体示例。假设有多个任务存在, 那么我们就创建了两个队列, 其中第一个队列专门用来维护这些任务, 另一个队列则负责存储已经完成了任务的日志信息。接下来的一步操作是要实例化进程, 以此来保证能够完成任务。像前面提到过的那样, 队列这个类别自身就具备同步功能了, 所以在进行相关工作时, 完全无需再去引用 Lock 这一种类来特别获取锁。在下面的这个示例里面, 我们将所有使用多进程功能的类全部合并整合到了一起。接下来请看下面的内容示例。示例 -from multiprocessing import Lock, Process, Queue, current_processimport timeimport queuedef jobTodo(tasks_to_perform, complete_tasks):while True:try:# The try block to catch task from the queue.# The get_nowait() function is used to# raise queue.Empty exception if the queue is empty.task tasks_to_perform.get_nowait()except queue.Empty:breakelse:# if no exception has been raised, the else block will execute# add the task completionprint(task)complete_tasks.put(task is done by current_process().name)time.sleep(.5)return Truedef main():total_task 8total_number_of_processes 3tasks_to_perform Queue()complete_tasks Queue()number_of_processes []for i in range(total_task):tasks_to_perform.put(Task no str(i))# defining number of processesfor w in range(total_number_of_processes):p Process(targetjobTodo, args(tasks_to_perform, complete_tasks))number_of_processes.append(p)p.start()# completing processfor p in number_of_processes:p.join()# print the outputwhile not complete_tasks.empty():print(complete_tasks.get())return Trueif __name__ __main__:main()输出Task no 2Task no 5Task no 0Task no 3Task no 6Task no 1Task no 4Task no 7Task no 0 is done by Process-1Task no 1 is done by Process-3Task no 2 is done by Process-2Task no 3 is done by Process-1Task no 4 is done by Process-3Task no 5 is done by Process-2Task no 6 is done by Process-1Task no 7 is done by Process-3中的多进程池对于在多个输入值上并行执行函数来说是必要的, 它还用于在进程之间分配输入数据也就是所谓的“数据并行性”。我们来考虑以下这一个使用了某个东西的池子的具体例子吧。示例 -from multiprocessing import Poolimport timew ([V, 5], [X, 2], [Y, 1], [Z, 3])def work_log(data_for_work):print( Process name is %s waiting time is %s seconds % (data_for_work[0], data_for_work[1]))time.sleep(int(data_for_work[1]))print( Process %s Executed. % data_for_work[0])def handler():p Pool(2)p.map(work_log, w)if __name__ __main__:handler()输出Process name is V waiting time is 5 secondsProcess V Executed.Process name is X waiting time is 2 secondsProcess X Executed.Process name is Y waiting time is 1 secondsProcess Y Executed.Process name is Z waiting time is 3 secondsProcess Z Executed.我们来了解一个关于池子的另一个例子。这个例子是编号为二的示例内容。from multiprocessing import Pooldef fun(x):return x*xif __name__ __main__:with Pool(5) as p:print(p.map(fun, [1, 2, 3]))输出[1, 8, 27]代理对象这种被称为共享对象的代理对象, 它驻留在完全不同的进程里面。同一个东西也被说成是代理。在很多情况下, 好几个不同的代理对象都可能指向完全相同的一个引用。这个代理对象本身是由多种方法集合起来构成的, 具体作用是负责去调用它所关联的那个原始对象的对应方法。下面就是关于代理对象的具体例子。示例 -from multiprocessing import Managermanager Manager()l manager.list([i*i for i in range(10)])print(l)print(repr(l))print(l[4])print(l[2:5])输出[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]16[4, 9, 16]代理这个对象的特征就是它具备可以被序列化的属性, 正因为如此, 所以我们能够在不同的进程之间去传递这些代理对象, 除此之外, 这些代理对象还被应用来控制同步级别的状况。多进程编程里面, 那些经常用到的函数。到目前为止, 咱们已经谈过了使用多进程编程的那个基本说法。做这个多进程的事情, 其实范围挺广的, 它在这个单一的机器里, 要做各种各样的任务, 这一点是特别重要的。下面是几个大家经常用到的东西, 这些都是在实现那些多进程工作的代码时, 关键的函数。方法描述执行名为pipe的操作。该函数的作用就是返回一对对象, 这两个对象之间建立了连接。run()这个方法是用于表现出进程中存在活动的, 所以叫做run()方法。start()这个方法的作用呢, 其实就是用来启动那个进程的, 具体的操作是通过调用start()来进行的。执行连接操作的步骤。join()这个方法是要让当前运行的那个东西停在那边, 别动, 一直等到它想要等的那个进程结束了才行, 也就是说会阻塞住进程, 一直到调用了join()方法的进程结束才会停止等待这个动作, 至于那个超时参数, 它是一个可以选的东西。()如果一个进程目前正处于活动的状态, 那么就返回True。()正如其名字表明的意思那般, 此函数是用来结束正在运行的程序的。需要大家务必留意的是, 在linux这个操作系统环境里面, 我们会采用那个没有填写具体名称的括号方法来进行操作而对于咱们前面提到的另一个对象或者系统而言, 则是调用同样留空了具体名称的另一个括号方法来完成任务的。
阅读完成 · 觉得有帮助?
咨询建站