一、进程与线程

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的

二、多进程multiprocessing

python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing。     multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。

  multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内

三、Process类的介绍

创建进程的类

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号==============================================
1 group参数未使用,值始终为None
2
3 target表示调用对象,即子进程要执行的任务
4
5 args表示调用对象的位置参数元组,args=(1,2,'egon',)
6
7 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
8
9 name为子进程的名称

方法介绍:

 1 p.start():启动进程,并调用该子进程中的p.run() 2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法  3 4 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁5 p.is_alive():如果p仍然运行,返回True6 7 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程 

属性介绍:

1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2
3 p.name:进程的名称
4
5 p.pid:进程的pid
6
7 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
8
9 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

四 Process类的使用

注意:在windows中Process()必须放到# if __name__ == '__main__':下

if __name__ == "__main__"
since statements inside this if-statement will not get called upon import.
由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。
如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。
这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。

创建并开启子进程的两种方式

#开进程的方法一:
import time
import random
from multiprocessing import Process
def piao(name):print('%s piaoing' %name)time.sleep(random.randrange(1,5))print('%s piao end' %name)p1=Process(target=piao,args=('egon',)) #必须加,号
p2=Process(target=piao,args=('alex',))
p3=Process(target=piao,args=('wupeqi',))
p4=Process(target=piao,args=('yuanhao',))p1.start()
p2.start()
p3.start()
p4.start()
print('主线程')方法一

方法一

#开进程的方法二:
import time
import random
from multiprocessing import Processclass Piao(Process):def __init__(self,name):super().__init__()self.name=namedef run(self):print('%s piaoing' %self.name)time.sleep(random.randrange(1,5))print('%s piao end' %self.name)p1=Piao('egon')
p2=Piao('alex')
p3=Piao('wupeiqi')
p4=Piao('yuanhao')p1.start() #start会自动调用run
p2.start()
p3.start()
p4.start()
print('主线程')方法二

方法二

练习1:把上周所学的socket通信变成并发的形式

from socket import *
from multiprocessing import Processserver=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)def talk(conn,client_addr):while True:try:msg=conn.recv(1024)if not msg:breakconn.send(msg.upper())except Exception:breakif __name__ == '__main__': #windows下start进程一定要写到这下面while True:conn,client_addr=server.accept()p=Process(target=talk,args=(conn,client_addr))p.start()server端

server端

from socket import *client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))while True:msg=input('>>: ').strip()if not msg:continueclient.send(msg.encode('utf-8'))msg=client.recv(1024)print(msg.decode('utf-8'))多个client端

每来一个客户端,都在服务端开启一个进程,如果并发来一个万个客户端,要开启一万个进程吗,你自己尝试着在你自己的机器上开启一万个,10万个进程试一试。
解决方法:进程池

Process对象的join方法

from multiprocessing import Process
import time
import randomclass Piao(Process):def __init__(self,name):self.name=namesuper().__init__()def run(self):print('%s is piaoing' %self.name)time.sleep(random.randrange(1,3))print('%s is piao end' %self.name)p=Piao('egon')
p.start()
p.join(0.0001) #等待p停止,等0.0001秒就不再等了
print('开始')join:主进程等,等待子进程结束

join:主进程等,等待子进程结束

from multiprocessing import Process
import time
import random
def piao(name):print('%s is piaoing' %name)time.sleep(random.randint(1,3))print('%s is piao end' %name)p1=Process(target=piao,args=('egon',))
p2=Process(target=piao,args=('alex',))
p3=Process(target=piao,args=('yuanhao',))
p4=Process(target=piao,args=('wupeiqi',))p1.start()
p2.start()
p3.start()
p4.start()#有的同学会有疑问:既然join是等待进程结束,那么我像下面这样写,进程不就又变成串行的了吗?
#当然不是了,必须明确:p.join()是让谁等?
#很明显p.join()是让主线程等待p的结束,卡住的是主线程而绝非进程p,#详细解析如下:
#进程只要start就会在开始运行了,所以p1-p4.start()时,系统中已经有四个并发的进程了
#而我们p1.join()是在等p1结束,没错p1只要不结束主线程就会一直卡在原地,这也是问题的关键
#join是让主线程等,而p1-p4仍然是并发执行的,p1.join的时候,其余p2,p3,p4仍然在运行,等#p1.join结束,可能p2,p3,p4早已经结束了,这样p2.join,p3.join.p4.join直接通过检测,无需等待
# 所以4个join花费的总时间仍然是耗费时间最长的那个进程运行的时间
p1.join()
p2.join()
p3.join()
p4.join()print('主线程')#上述启动进程与join进程可以简写为
# p_l=[p1,p2,p3,p4]
#
# for p in p_l:
#     p.start()
#
# for p in p_l:
#     p.join()

有了join,程序不就是串行了吗???

有了join,程序不就是串行了吗???

Process对象的其他方法或属性(了解)

返回主页
吾日三洗吾身,洗头、洗脸、洗身体博客园首页新随笔联系订阅管理随笔 - 29  文章 - 52  评论 - 37
python并发编程之多进程阅读目录一 multiprocessing模块介绍二 Process类的介绍三 Process类的使用四 守护进程五 进程同步(锁)六 队列(推荐使用)七 管道八 共享数据九 信号量(了解)十 事件(了解)十一 进程池回到顶部
一 multiprocessing模块介绍python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程。Python提供了multiprocessing。multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。
回到顶部
二 Process类的介绍创建进程的类:Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号参数介绍:
复制代码1 group参数未使用,值始终为None
2
3 target表示调用对象,即子进程要执行的任务
4
5 args表示调用对象的位置参数元组,args=(1,2,'egon',)
6
7 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
8
9 name为子进程的名称复制代码方法介绍:
复制代码1 p.start():启动进程,并调用该子进程中的p.run() 2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法  3 4 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁5 p.is_alive():如果p仍然运行,返回True6 7 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程  复制代码属性介绍:
复制代码1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2
3 p.name:进程的名称
4
5 p.pid:进程的pid
6
7 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
8
9 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)复制代码
回到顶部
三 Process类的使用注意:在windows中Process()必须放到# if __name__ == '__main__':下
复制代码Since Windows has no fork, the multiprocessing module starts a new Python process and imports the calling module.
If Process() gets called upon import, then this sets off an infinite succession of new processes (or until your machine runs out of resources).
This is the reason for hiding calls to Process() insideif __name__ == "__main__"
since statements inside this if-statement will not get called upon import.
由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。
如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。
这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。复制代码创建并开启子进程的两种方式
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码练习1:把上周所学的socket通信变成并发的形式
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码每来一个客户端,都在服务端开启一个进程,如果并发来一个万个客户端,要开启一万个进程吗,你自己尝试着在你自己的机器上开启一万个,10万个进程试一试。
解决方法:进程池Process对象的join方法
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码Process对象的其他方法或属性(了解)
复制代码#进程对象的其他方法一:terminate,is_alive
from multiprocessing import Process
import time
import randomclass Piao(Process):def __init__(self,name):self.name=namesuper().__init__()def run(self):print('%s is piaoing' %self.name)time.sleep(random.randrange(1,5))print('%s is piao end' %self.name)p1=Piao('egon1')
p1.start()p1.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
print(p1.is_alive()) #结果为Trueprint('开始')
print(p1.is_alive()) #结果为False

复制代码
name与pid
回到顶部
四 守护进程主进程创建守护进程其一:守护进程会在主进程代码执行结束后就终止其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
View Code
迷惑人的例子
回到顶部
五 进程同步(锁)进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,竞争带来的结果就是错乱,如何控制,就是加锁处理part1:多个进程共享同一打印终端
并发运行,效率高,但竞争同一打印终端,带来了打印错乱
加锁:由并发变成了串行,牺牲了运行效率,但避免了竞争part2:多个进程共享同一文件文件当数据库,模拟抢票
并发运行,效率高,但竞争写同一文件,数据写入错乱
加锁:购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全总结:加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以用文件共享数据实现进程间通信,但问题是:
1.效率低
2.需要自己加锁处理为此mutiprocessing模块为我们提供了基于消息的IPC通信机制:队列和管道。
1 队列和管道都是将数据存放于内存中
2 队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,
我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。
回到顶部
六 队列(推荐使用)进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的创建队列的类(底层就是以管道和锁定的方式实现):1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。 参数介绍:1 maxsize是队列中允许最大项数,省略则无大小限制。    方法介绍:主要方法:
复制代码
复制代码1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
3
4 q.get_nowait():同q.get(False)
5 q.put_nowait():同q.put(False)
6
7 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
8 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
9 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样复制代码
复制代码其他方法(了解):1 q.cancel_join_thread():不会在进程退出时自动连接后台线程。可以防止join_thread()方法阻塞
2 q.close():关闭队列,防止队列中加入更多数据。调用此方法,后台线程将继续写入那些已经入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将调用此方法。关闭队列不会在队列使用者中产生任何类型的数据结束信号或异常。例如,如果某个使用者正在被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
3 q.join_thread():连接队列的后台线程。此方法用于在调用q.close()方法之后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread方法可以禁止这种行为应用:
View Code生产者消费者模型在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。为什么要使用生产者和消费者模式在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。什么是生产者消费者模式生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。基于队列实现生产者消费者模型
View Code此时的问题是主进程永远不会结束,原因是:生产者p在生产完后就结束了,但是消费者c在取空了q之后,则一直处于死循环中且卡在q.get()这一步。解决方式无非是让生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环
生产者在生产完毕后发送结束信号None注意:结束信号None,不一定要由生产者发,主进程里同样可以发,但主进程需要等生产者结束后才应该发送该信号
主进程在生产者生产完毕后发送结束信号None但上述解决方式,在有多个生产者和多个消费者时,我们则需要用一个很low的方式去解决
有几个生产者就需要发送几次结束信号:相当low其实我们的思路无非是发送结束信号而已,有另外一种队列提供了这种机制
复制代码#JoinableQueue([maxsize]):这就像是一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。#参数介绍:
    maxsize是队列中允许最大项数,省略则无大小限制。    #方法介绍:
    JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止复制代码
View Code
回到顶部
七 管道进程间通信(IPC)方式二:管道(不推荐使用,了解即可)
介绍
基于管道实现进程间通信(与队列的方式是类似的,队列就是管道加锁实现的)注意:生产者和消费者都没有使用管道的某个端点,就应该将其关闭,如在生产者中关闭管道的右端,在消费者中关闭管道的左端。如果忘记执行这些步骤,程序可能再消费者中的recv()操作上挂起。管道是由操作系统进行引用计数的,必须在所有进程中关闭管道后才能生产EOFError异常。因此在生产者中关闭管道不会有任何效果,付费消费者中也关闭了相同的管道端点。
管道可以用于双向通信,利用通常在客户端/服务器中使用的请求/响应模型或远程过程调用,就可以使用管道编写与进程交互的程序
回到顶部
八 共享数据展望未来,基于消息传递的并发编程是大势所趋即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合通过消息队列交换数据。这样极大地减少了对使用锁定和其他同步手段的需求,还可以扩展到分布式系统中进程间通信应该尽量避免使用本节所讲的共享数据的方式
复制代码进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array. For example,复制代码
进程之间操作共享的数据
回到顶部
九 信号量(了解)
信号量Semahpore(同线程一样)
回到顶部
十 事件(了解)
Event(同线程一样)
回到顶部
十一 进程池在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。多进程是实现并发的手段之一,需要注意的问题是:很明显需要并发执行的任务通常要远大于核数一个操作系统不可能无限开启进程,通常有几个核就开几个进程进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)例如当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个。。。手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。我们就可以通过维护一个进程池来控制进程数目,比如httpd的进程模式,规定最小进程数和最大进程数...
ps:对于远程过程调用的高级应用程序而言,应该使用进程池,Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程。创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个进程去执行所有任务,不会开启其他进程1 Pool([numprocess  [,initializer [, initargs]]]):创建进程池 参数介绍:1 numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
2 initializer:是每个工作进程启动时要执行的可调用对象,默认为None
3 initargs:是要传给initializer的参数组方法介绍:主要方法:
复制代码1 p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()
2 p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。
3
4 p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
5 P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用复制代码其他方法(了解部分)
View Code 应用:
apply同步执行:阻塞式
apply_async异步执行:非阻塞
详解:apply_async与apply练习2:使用进程池维护固定数目的进程(重写练习1)
server端
客户端发现:并发开启多个客户端,服务端同一时间只有3个不同的pid,干掉一个客户端,另外一个客户端才会进来,被3个进程之一处理回掉函数:需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。
View Code
爬虫案例如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数
View Code进程池的其他实现方式:https://docs.python.org/dev/library/concurrent.futures.html好文要顶 关注我 收藏该文
linhaifeng
关注 - 2
粉丝 - 372
+加关注
0
0
« 上一篇:Python开发之路
posted @ 2017-08-25 17:03 linhaifeng 阅读(291) 评论(0)  编辑 收藏
刷新评论刷新页面返回顶部
(评论功能已被禁用)
【推荐】50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
【推荐】可嵌入您系统的“在线Excel”!SpreadJS 纯前端表格控件
阿里云0830
最新IT新闻:
· 苹果AirPods拿下无线耳机市场近85%的销售额
· 三星获加州自动驾驶汽车测试许可 称暂无造车计划
· 任天堂Wii体感手柄被判侵权 需赔偿1000万美元
· 今日起正式取消手机长途和漫游费 但降费仍在路上
· 苹果遭反垄断举报 又一家律师事务所加入讨伐阵营
» 更多新闻...
极光0801
最新知识库文章:
· 做到这一点,你也可以成为优秀的程序员
· 写给立志做码农的大学生
· 架构腐化之谜
· 学会思考,而不只是编程
· 编写Shell脚本的最佳实践
» 更多知识库文章...
公告
林海峰的头像
昵称:linhaifeng
园龄:10个月
粉丝:372
关注:2
+加关注
<    2017年9月    >
日    一    二    三    四    五    六
27    28    29    30    31    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
1    2    3    4    5    6    7
搜索常用链接我的随笔我的评论我的参与最新评论我的标签随笔分类openstack系列(18)shell script(3)编程基础(4)随笔档案2017年8月 (1)2017年4月 (3)2017年3月 (11)2017年2月 (6)2017年1月 (8)文章分类python全栈之linux基础(1)python全栈之操作系统(1)python全栈之网络基础(3)相册头像(1)python开发之路
最新评论1. Re:Cpython解释器支持的进程与线程峰哥,我也喜欢听吴赫的声音,也爱看《无限挑战》歌谣祭,也喜欢GAI的方言嘻哈。哈哈哈。。。。--jaylg2. Re:Cpython解释器支持的进程与线程峰哥这个歌曲评论峰回路转--jaylg3. Re:进程与线程理论基础pretty good--linhaifeng教我学python4. Re:linux基础摸摸哒。。。--人到中年万事休5. Re:第十三篇 python基础之socket编程峰哥真jb屌--linhaifeng教我学python阅读排行榜1. Python开发之路(2405)2. 计算机基础系列一:计算机硬件(1447)3. 操作系统简介(1411)4. openstack系列阅读目录(1255)5. Shell第二篇:正则表达式和文本处理工具(1026)评论排行榜1. openstack系列介绍(6)2. openstack系列阅读目录(3)3. 计算机基础系列一:计算机硬件(1)4. 计算机硬件发展史(1)5. glance系列二:glance部署及操作(1)推荐排行榜1. 操作系统简介(6)2. 计算机基础系列一:计算机硬件(5)3. keystone系列一:keystone基础(4)4. nova系列二:kvm介绍(3)5. Python开发之路(3)Copyright ©2017 linhaifeng

terminate与is_alive

from multiprocessing import Process
import time
import random
class Piao(Process):def __init__(self,name):# self.name=name# super().__init__() #Process的__init__方法会执行self.name=Piao-1,#                    #所以加到这里,会覆盖我们的self.name=name#为我们开启的进程设置名字的做法super().__init__()self.name=namedef run(self):print('%s is piaoing' %self.name)time.sleep(random.randrange(1,3))print('%s is piao end' %self.name)p=Piao('egon')
p.start()
print('开始')
print(p.pid) #查看pid

name与pid

name与pid

四 守护进程

主进程创建守护进程

  其一:守护进程会在主进程代码执行结束后就终止

  其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

from multiprocessing import Process
import time
import randomclass Piao(Process):def __init__(self,name):self.name=namesuper().__init__()def run(self):print('%s is piaoing' %self.name)time.sleep(random.randrange(1,3))print('%s is piao end' %self.name)p=Piao('egon')
p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行
p.start()
print('主')

#主进程代码运行完毕,守护进程就会结束
from multiprocessing import Process
from threading import Thread
import time
def foo():print(123)time.sleep(1)print("end123")def bar():print(456)time.sleep(3)print("end456")p1=Process(target=foo)
p2=Process(target=bar)p1.daemon=True
p1.start()
p2.start()
print("main-------") #打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止

迷惑人的例子

迷惑人的例子

五 进程同步(锁)

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,

竞争带来的结果就是错乱,如何控制,就是加锁处理

part1:多个进程共享同一打印终端

#并发运行,效率高,但竞争同一打印终端,带来了打印错乱
from multiprocessing import Process
import os,time
def work():print('%s is running' %os.getpid())time.sleep(2)print('%s is done' %os.getpid())if __name__ == '__main__':for i in range(3):p=Process(target=work)p.start()并发运行,效率高,但竞争同一打印终端,带来了打印错乱

并发运行,效率高,但竞争同一打印终端,带来了打印错乱

#由并发变成了串行,牺牲了运行效率,但避免了竞争
from multiprocessing import Process,Lock
import os,time
def work(lock):lock.acquire()print('%s is running' %os.getpid())time.sleep(2)print('%s is done' %os.getpid())lock.release()
if __name__ == '__main__':lock=Lock()for i in range(3):p=Process(target=work,args=(lock,))p.start()加锁:由并发变成了串行,牺牲了运行效率,但避免了竞争

加锁:由并发变成了串行,牺牲了运行效率,但避免了竞争

part2:多个进程共享同一文件

文件当数据库,模拟抢票

#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
from multiprocessing import Process,Lock
import time,json,random
def search():dic=json.load(open('db.txt'))print('\033[43m剩余票数%s\033[0m' %dic['count'])def get():dic=json.load(open('db.txt'))time.sleep(0.1) #模拟读数据的网络延迟if dic['count'] >0:dic['count']-=1time.sleep(0.2) #模拟写数据的网络延迟json.dump(dic,open('db.txt','w'))print('\033[43m购票成功\033[0m')def task(lock):search()get()
if __name__ == '__main__':lock=Lock()for i in range(100): #模拟并发100个客户端抢票p=Process(target=task,args=(lock,))p.start()并发运行,效率高,但竞争写同一文件,数据写入错乱

#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
from multiprocessing import Process,Lock
import time,json,random
def search():dic=json.load(open('db.txt'))print('\033[43m剩余票数%s\033[0m' %dic['count'])def get():dic=json.load(open('db.txt'))time.sleep(0.1) #模拟读数据的网络延迟if dic['count'] >0:dic['count']-=1time.sleep(0.2) #模拟写数据的网络延迟json.dump(dic,open('db.txt','w'))print('\033[43m购票成功\033[0m')def task(lock):search()get()
if __name__ == '__main__':lock=Lock()for i in range(100): #模拟并发100个客户端抢票p=Process(target=task,args=(lock,))p.start()并发运行,效率高,但竞争写同一文件,数据写入错乱

加锁:购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全

六 队列(推荐使用)

进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的

 创建队列的类(底层就是以管道和锁定的方式实现)

1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。 

 参数介绍:

1 maxsize是队列中允许最大项数,省略则无大小限制。    

方法介绍:

主要方法:
1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
3
4 q.get_nowait():同q.get(False)
5 q.put_nowait():同q.put(False)
6
7 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
8 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
9 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

其他方法(了解):

1 q.cancel_join_thread():不会在进程退出时自动连接后台线程。可以防止join_thread()方法阻塞
2 q.close():关闭队列,防止队列中加入更多数据。调用此方法,后台线程将继续写入那些已经入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将调用此方法。关闭队列不会在队列使用者中产生任何类型的数据结束信号或异常。例如,如果某个使用者正在被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
3 q.join_thread():连接队列的后台线程。此方法用于在调用q.close()方法之后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread方法可以禁止这种行为

 应用:

'''
multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的,但是队列接口
'''from multiprocessing import Process,Queue
import time
q=Queue(3)#put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
print(q.full()) #满了print(q.get())
print(q.get())
print(q.get())
print(q.empty()) #空了

八 共享数据

进程间通信应该尽量避免使用本节所讲的共享数据的方式

进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array. For example,

from multiprocessing import Manager,Process,Lock
import os
def work(d,lock):# with lock: #不加锁而操作共享的数据,肯定会出现数据错乱d['count']-=1if __name__ == '__main__':lock=Lock()with Manager() as m:dic=m.dict({'count':100})p_l=[]for i in range(100):p=Process(target=work,args=(dic,lock))p_l.append(p)p.start()for p in p_l:p.join()print(dic)#{'count': 94}

进程之间操作共享的数据

十一 进程池

在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。多进程是实现并发的手段之一,需要注意的问题是:

  1. 很明显需要并发执行的任务通常要远大于核数
  2. 一个操作系统不可能无限开启进程,通常有几个核就开几个进程
  3. 进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)

例如当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个。。。手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。

我们就可以通过维护一个进程池来控制进程数目,比如httpd的进程模式,规定最小进程数和最大进程数... ps:对于远程过程调用的高级应用程序而言,应该使用进程池,Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程。

    创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个进程去执行所有任务,不会开启其他进程

 Pool([numprocess  [,initializer [, initargs]]]):创建进程池 

  参数介绍:

1 numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
2 initializer:是每个工作进程启动时要执行的可调用对象,默认为None
3 initargs:是要传给initializer的参数组

方法介绍:

主要方法:
p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()
p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用

回掉函数:

需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数

我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。

from multiprocessing import Pool
import requests
import json
import osdef get_page(url):print('<进程%s> get %s' %(os.getpid(),url))respone=requests.get(url)if respone.status_code == 200:return {'url':url,'text':respone.text}def pasrse_page(res):print('<进程%s> parse %s' %(os.getpid(),res['url']))parse_res='url:<%s> size:[%s]\n' %(res['url'],len(res['text']))with open('db.txt','a') as f:f.write(parse_res)if __name__ == '__main__':urls=['https://www.baidu.com','https://www.python.org','https://www.openstack.org','https://help.github.com/','http://www.sina.com.cn/']p=Pool(3)res_l=[]for url in urls:res=p.apply_async(get_page,args=(url,),callback=pasrse_page)res_l.append(res)p.close()p.join()print([res.get() for res in res_l]) #拿到的是get_page的结果,其实完全没必要拿该结果,该结果已经传给回调函数处理了'''
打印结果:
<进程3388> get https://www.baidu.com
<进程3389> get https://www.python.org
<进程3390> get https://www.openstack.org
<进程3388> get https://help.github.com/
<进程3387> parse https://www.baidu.com
<进程3389> get http://www.sina.com.cn/
<进程3387> parse https://www.python.org
<进程3387> parse https://help.github.com/
<进程3387> parse http://www.sina.com.cn/
<进程3387> parse https://www.openstack.org
[{'url': 'https://www.baidu.com', 'text': '<!DOCTYPE html>\r\n...',...}]
'''

from multiprocessing import Pool
import time,random
import requests
import redef get_page(url,pattern):response=requests.get(url)if response.status_code == 200:return (response.text,pattern)def parse_page(info):page_content,pattern=infores=re.findall(pattern,page_content)for item in res:dic={'index':item[0],'title':item[1],'actor':item[2].strip()[3:],'time':item[3][5:],'score':item[4]+item[5]}print(dic)
if __name__ == '__main__':pattern1=re.compile(r'<dd>.*?board-index.*?>(\d+)<.*?title="(.*?)".*?star.*?>(.*?)<.*?releasetime.*?>(.*?)<.*?integer.*?>(.*?)<.*?fraction.*?>(.*?)<',re.S)url_dic={'http://maoyan.com/board/7':pattern1,}p=Pool()res_l=[]for url,pattern in url_dic.items():res=p.apply_async(get_page,args=(url,pattern),callback=parse_page)res_l.append(res)for i in res_l:i.get()# res=requests.get('http://maoyan.com/board/7')# print(re.findall(pattern,res.text))

爬虫案例

如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数

from multiprocessing import Pool
import time,random,osdef work(n):time.sleep(1)return n**2
if __name__ == '__main__':p=Pool()res_l=[]for i in range(10):res=p.apply_async(work,args=(i,))res_l.append(res)p.close()p.join() #等待进程池中所有进程执行完毕
nums=[]for res in res_l:nums.append(res.get()) #拿到所有结果print(nums) #主进程拿到所有的处理结果,可以在主进程中进行统一进行处理

转载于:https://www.cnblogs.com/wangshaojie/p/7461881.html

day9 进程 协程相关推荐

  1. python线程进程协程面试_Python学习经验之谈:关于协程的理解和其相关面试问题...

    都知道Python非常适合初学者学习来入门编程,昨天有伙伴留言说面试了Python岗位,问及了一个关于协程的问题,想了想还是跟大家出一篇协程相关的文章和在Python面试中可能会问及的相关面试问题.都 ...

  2. 打开线程 | 进程 | 协程的大门

    不知从几何起,可能是大三那年的操作系统考试,也可能是刚经历完的秋招,这些概念总是迷迷糊糊,可能自己回答的和其他人的答复也差不多,并没有什么亮点,通常都会以:「我们换个题」的方式结束,有时候也挺尴尬的. ...

  3. python进程\协程\异步IO

    进程 学习python中有什么不懂的地方,小编这里推荐加小编的python学习群:895 817 687有任何不懂的都可以在里面交流,还有很好的视频教程pdf学习资料,大家一起学习交流! Python ...

  4. python异步和进程_12.python进程\协程\异步IO

    进程 Python中的多线程无法利用多核优势 , 所以如果我们想要充分地使用多核CPU的资源 , 那么就只能靠多进程了 multiprocessing模块中提供了Process , Queue , P ...

  5. python协程和线程_python之并发编程(线程\进程\协程)

    一.进程和线程 1.进程 假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源.是 ...

  6. python apply_async死锁_python之并发编程(线程\进程\协程)

    一.进程和线程 1.进程假如有两个程序A和B,程序A在执行到一半的过程中,需要读取大量的数据输入(I/O操作),而此时CPU只能静静地等待任务A读取完数据才能继续执行,这样就白白浪费了CPU资源.是不 ...

  7. python_21_线程+进程+协程

    python_线程_进程_协程 什么是线程? -- os能够进行运算调度的最小单位,被包含在进程之中,是一串指令的集合 -- 每个线程都是独立的,可以访问同一进程下所有的资源 什么是进程? -- 每个 ...

  8. 十四丶并发编程(线程 进程 协程)

    Yuan先生 知识预览 操作系统 回到顶部 操作系统 一 为什么要有操作系统? 现代计算机系统是由一个或者多个处理器,主存,磁盘,打印机,键盘,鼠标显示器,网络接口以及各种其他输入输出设备组成的复杂系 ...

  9. 进程/协程/简述进程,线程的优缺点?

    进程 1.含义? 一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元. 简述进程,线程的优缺点? 首先线程是cpu执行的最小单元,进程包含线程,每一个进程下可以有多个线程 ...

最新文章

  1. 戴尔电脑管家_2020年笔记本电脑推荐指南:笔记本电脑应该怎么选?什么牌子的笔记本电脑更值得入手?...
  2. tf.keras.layers.Flatten() 示例
  3. 怎么计算一组数据的波动_数据分析(一):数据描述统计
  4. zookeeper源码分析之三客户端发送请求流程
  5. popwindow setFocusable(false) 不消失与弹出软键盘的冰火不容的矛盾
  6. 【PMP学习笔记】:二、项目运行环境
  7. POJ 1183 反正切函数的应用(数学代换,基本不等式)
  8. Spark基础学习笔记04:搭建Spark伪分布式环境
  9. canvas图形绘制
  10. PL/SQL 实现行列转换
  11. 不能错过!CIO不可不知的“数据经济学” | 凌云时刻
  12. day24-XSS过滤及单实例
  13. 随机游走算法(Random Walk)
  14. 人工智能60年:图灵的在天之灵,安息吧!
  15. 毕业设计--智能智造协同管理平台
  16. 通过身份证号码得到性别和出生日期
  17. 入行大数据,需要学习哪些基础知识?
  18. MySQL如何同时删除主外键关联的两张表中的数据
  19. 一分钟了解英语表达,作为上一段话的总结:综上所述
  20. Prim 算法的实现

热门文章

  1. 实现Linux下的cp命令
  2. 进击的局座:悄悄话读后感
  3. dive into python 3_对象方法Dive into Python读书笔记3
  4. java调用海康威视sdk获取车牌号demo
  5. 2022最新软件测试面试八股文,全网最全最新,堪称地表最强
  6. 计算机毕业设计安卓App毕设项目之springboot+合同管理APPNodejs毕业设计计算机项目源码
  7. 数据科学与大数据分析之项目3-关联规则
  8. SQLyog连接本地数据库报2003错误
  9. 银粉和薄片行业调研报告 - 市场现状分析与发展前景预测
  10. C#中读取XML文件方法