进程的创建和结束:

multiprocess模块:

multiprocess不是一个模块而是python中一个操作、管理进程的包

分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号 参数介绍:
1 group参数未使用,值始终为None
2 target表示调用对象,即子进程要执行的任务
3 args表示调用对象的位置参数元组,args=(1,2,'egon',)
4 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
5 name为子进程的名称
1 p.start():启动进程,并调用该子进程中的p.run()
2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
3 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
4 p.is_alive():如果p仍然运行,返回True
5 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 p.name:进程的名称
3 p.pid:进程的pid
4 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
5 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

属性介绍

在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’ 判断保护起来,import 的时候  ,就不会递归运行了。

windows下操作进程

使用process模块创建进程

启动两个进程(可以用for循环启动多个进程)

from multiprocessing import Process

def func(name):
print(666)
print('我是子进程', name) if __name__ == '__main__':
p1 = Process(target=func, args=('alex',)) # 传的必须是元组,所以alex后面要逗号
p2 = Process(target=func, args=('bob',)) # 传的必须是元组,所以alex后面要逗号
p1.start() # 第一个进程
p2.start() # 第二个进程
print('我是主进程'))

join用法:

from multiprocessing import Process
import time def func(a):
time.sleep(2)
print('子进程', a) if __name__ == '__main__':
p = Process(target=func, args=(1,))
p.start() # 把自己程交给CPU,就开始执行下面的代码。非阻塞态不用等
p.join() # 等待子进程运行完后,父进程才运行,父进程要回收子进程内存空间
print('主进程')

join用法

查看子进程和父进程 id: 子进程:os.getpid   父进程:os.getppid

from multiprocessing import Process
import time
import os def func(a):
time.sleep(2)
print('子进程', os.getpid()) # os.getpid获得子进程ID
print('父进程', os.getppid()) # os.getppid 获得主进程ID if __name__ == '__main__':
print('主进程ID', os.getpid()) # os.getpid获得主进程ID
p = Process(target=func, args=(1,))
p.start()

同时启动多个子进程:

from multiprocessing import Process
import time
import os def func(a):
time.sleep(1)
print('子进程ID', os.getpid()) if __name__ == '__main__':
for i in range(5): # 启动五个子进程
p = Process(target=func, args=(1,))
p.start()
print('主进程ID', os.getppid())

多个子进程中join的用法:

from multiprocessing import Process
import os def func(a):
print('子进程ID%s'% a, os.getpid()) if __name__ == '__main__':
for i in range(5): # 启动五个子进程
p = Process(target=func, args=(i,))
p.start()
# p.join() # 放这里会等待第一个子进程结束后,在执行下一个子进程。不能实现同时启动运行
p.join() #放这里,只会等待进程4结束后,就执行下面的程序。此时可能进程1,2,3并没有执行完。无法达到阻隔子进程的效果
print('主进程ID', os.getppid())

简单版

from multiprocessing import Process

def func(a):
time.sleep(0.3)
print('子进程%s正在发发邮件' % a) if __name__ == '__main__':
p_lis = []
for i in range(5): # 启动五个子进程
p = Process(target=func, args=(i,))
p.start()
p_lis.append(p)
for i in p_lis:
p.join() #遍历列表,列表里面是每一个进程。都会被join阻隔
print('所有邮件都已经发出') # 等所有进程都给join阻隔完后,才运行

升级版,发邮件例子

进程间的数据隔离:

进程间数据是相互隔离的

from multiprocessing import Process
n = 100
def func():
global n
for i in range(5):
n = n - 1
print(n) # 每一个进程执行的结果都是 95,说明子进程间彼此隔离
if __name__ == '__main__':
p_lst = []
for i in range(10):
p = Process(target=func)
p.start()
p_lst.append(p)
for p in p_lst:
p.join()
print(n) #

数据隔离,和进程间隔离

开启进程的另一种方式: 继承Process类

class Myprocess(Process):
def run(self):
print('子进程',os.getpid()) if __name__ == '__main__':
p = Myprocess()
p.start()
print('主进程', os.getppid())

固定格式

class Myprocess(Process):
def __init__(self,arg): # 用于接收参数
super().__init__() # 必须执行父类的init方法
self.arg = arg
def run(self):
print('子进程',os.getpid(), self.arg) # 使用参数 if __name__ == '__main__':
p = Myprocess('我是参数') # 实例化是传输参数
p.start()
print('主进程', os.getppid())

带参数形式

多进程中的其他方法:

p1.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
print(p1.is_alive()) #判断进程是否存活,结果为True or False
from multiprocessing import Process
import time
class Myprocess(Process):
def __init__(self,arg):
super().__init__()
self.arg = arg
def run(self):
print('子进程',os.getpid(), self.arg) if __name__ == '__main__':
p = Myprocess('我是参数')
p.start()
print(p.is_alive()) # 判断进程是否存活,返回True,False
print('主进程', os.getppid())
p.terminate() # 关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
time.sleep(1)
print(p.is_alive())

多进程中的其他方法

守护进程:

# 1.守护进程 会随着主进程代码的结束而结束
# 2.守护进程不会守护除了主进程代码之外的其他子进程
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
p.daemon = True # 定义p为守护进程
import time
from multiprocessing import Process def guard():
while True:
print('我是守护进程,正在工作') # 只守护主进程
time.sleep(1) def main_main():
print('我不知道自己是不是主进程,正在运行....')
time.sleep(5)
print('结束') def func():
print('我不知道自己是不是主进程')
time.sleep(3)
print('func结束了') if __name__ == '__main__':
p = Process(target=guard)
p.daemon = True # 定义一个守护进程
p.start()
print('大结局') # 这也是主进程
# p2 = Process(target=func())
# p2.start() # 如果用Process启动,则是子进程
p1 = Process(target=main_main) # 非主进程
p1.start()
func() # 这是主进程

守护进程

多进程 实现socket tcp协议 server端的并发

import socket
from multiprocessing import Process def func(conn):
while True:
conn.send(b'hello') if __name__ == '__main__':
sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()
while True:
conn,addr = sk.accept()
p = Process(target=func,args = (conn,))
p.start()

server端


import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9000)) while True:
msg = sk.recv(1024)
print(msg)

client端


进程同步: (multiprocess.Lock), 抢票例子

保证数据安全,会让程序重新变成串行,浪费时间

# 多个进程 抢占同一个数据资源 会造成 数据不安全
# 我们必须要牺牲效率来保证数据的安全性
import json
import time
from multiprocessing import Lock
from multiprocessing import Process
def search(name):
with open('ticket') as f:
ticket_count = json.load(f)
if ticket_count['count'] >=1:
print('%s : 有余票%s张'%(name,ticket_count['count']))
else:
print('%s : 没票了'%name) def buy(name):
with open('ticket') as f:
ticket_count = json.load(f)
time.sleep(0.2)
if ticket_count['count'] >=1:
print('有余票%s张'%ticket_count['count'])
ticket_count['count'] -= 1
print('%s买到票了'%name)
else:
print('%s没买到票' % name)
time.sleep(0.2)
with open('ticket','w') as f:
json.dump(ticket_count,f) def opt(lock,name):
search(name)
lock.acquire() # 拿走钥匙
buy(name)
lock.release() # 归还钥匙 if __name__ == '__main__':
lock = Lock() # 锁 互斥锁
for i in range(10):
p = Process(target=opt,args = (lock,'alex'+str(i),))
p.start()

抢票例子

#加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以用文件共享数据实现进程间通信,但问题是:
1.效率低(共享数据基于文件,而文件是硬盘上的数据)
2.需要自己加锁处理 #因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。
队列和管道都是将数据存放于内存中
队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,
我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。

队列(multiprocess.Queue)

IPC: 进程间通信

q.get( [ block [ ,timeout ] ] )
返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。block用于控制阻塞行为,默认为True. 如果设置为False,将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。 q.put(item [, block [,timeout ] ] )
将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)。timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。 q.qsize()
返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。 q.close()
关闭队列,防止队列中加入更多数据。调用此方法时,后台线程将继续写入那些已入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将自动调用此方法。关闭队列不会在队列使用者中生成任何类型的数据结束信号或异常。例如,如果某个使用者正被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。

队列参数

生产者消费者模型:

import time
from multiprocessing import Process, Queue def consumer(name,q):
while True:
food = q.get()
if not food: break
time.sleep(1)
print('%s吃了一个%s' % (name, food)) def producer(q, food_name):
for i in range(20):
time.sleep(0.1)
food='%s%s' % (food_name, i )
print('制造了%s' % food)
q.put(food) if __name__ == '__main__':
q = Queue(5)
p1 = Process(target=consumer, args=('alex',q))
p2 = Process(target=consumer, args=('wusir',q))
p3 = Process(target=producer, args=(q, '泔水'))
p1.start()
p2.start()
p3.start()
p2.join()
q.put(None)
q.put(None)

生产消费者模型

数据共享   Manager

进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的
虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此
from multiprocessing import Manager,Process,Lock
def work(d,lock):
with lock: #不加锁而操作共享的数据,肯定会出现数据错乱
d['count']-=1 if __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) Manager例子

进程池:  multiprocessing.Pool

定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行。这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果。

import os
import time
from multiprocessing import Pool def func(i):
time.sleep(0.1)
print(i,os.getpid()) if __name__ == '__main__':
p = Pool(4) # cpu个数 + 1/cpu的个数
for i in range(10):
p.apply_async(func,args=(i,)) # async异步的提交任务
p.close() # 关闭池子,不是要回收池子中的进程,而是阻止继续向池子中提交任务
p.join() # 阻塞,直到池子中的任务都执行完毕

进程池

起多进程的意义
# 1.为了更好的利用CPU,所以如果我们的程序中都是网络IO,文件IO就不适合起多进程
# 2.为了数据的隔离,如果我们的程序中总是要用到数据共享,那么就不适合使用多进程
# 3.超过了cpu个数的任务数,都应该使用进程池来解决问题,而不能无限的开启子进程
Pool([numprocess  [,initializer [, initargs]]]):创建进程池
numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
2 initializer:是每个工作进程启动时要执行的可调用对象,默认为None
3 initargs:是要传给initializer的参数组

参数介绍

 p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执func(*args,**kwargs),然后返回结果。
p.close():关闭进程池,防止进一步操作
P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用

主要方法

多进程与进程池性能测试:

import os
import time
from multiprocessing import Pool,Process def func(i):
print(i,os.getpid()) if __name__ == '__main__':
start = time.time()
p_lst = []
for i in range(100):
p = Process(target=func,args = (i,))
p.start()
p_lst.append(p)
for p in p_lst:
p.join()
end = time.time()
pro_time = end-start
start = time.time()
p = Pool(4)
for i in range(100):
p.apply_async(func,args=(i,)) # async异步的提交任务
p.close() # 关闭池子,不是要回收池子中的进程,而是阻止继续向池子中提交任务
p.join() # 阻塞,直到池子中的任务都执行完毕
end = time.time()
pool_time = end - start
print(pro_time,pool_time)

同步和异步:

import os,time
from multiprocessing import Pool def work(n):
print('%s run' %os.getpid())
time.sleep(3)
return n**2 if __name__ == '__main__':
p=Pool(3) #进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
res_l=[]
for i in range(10):
res=p.apply(work,args=(i,)) # 同步调用,直到本次任务执行完毕拿到res,等待任务work执行的过程中可能有阻塞也可能没有阻塞
# 但不管该任务是否存在阻塞,同步调用都会在原地等着
print(res_l) 进程池的同步调用

进程池的同步调用

import os
import time
import random
from multiprocessing import Pool def work(n):
print('%s run' %os.getpid())
time.sleep(random.random())
return n**2 if __name__ == '__main__':
p=Pool(3) #进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
res_l=[]
for i in range(10):
res=p.apply_async(work,args=(i,)) # 异步运行,根据进程池中有的进程数,每次最多3个子进程在异步执行
# 返回结果之后,将结果放入列表,归还进程,之后再执行新的任务
# 需要注意的是,进程池中的三个进程不会同时开启或者同时结束
# 而是执行完一个就释放一个进程,这个进程就去接收新的任务。
res_l.append(res) # 异步apply_async用法:如果使用异步提交的任务,主进程需要使用jion,等待进程池内任务都处理完,然后可以用get收集结果
# 否则,主进程结束,进程池可能还没来得及执行,也就跟着一起结束了
p.close()
p.join()
for res in res_l:
print(res.get()) #使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get 进程池的异步调用

进程池的异步调用

回调函数:

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

我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果。
import time
import random
from multiprocessing import Process,Pool
def get(i): # 进程池的子进程执行的
time.sleep(random.random())
print('从网页获取一个网页的内容', i)
return i,'网页的内容'*i def call_back(content): # 主进程执行的
print(content) if __name__ == '__main__':
# p = Process(target=get)
# p.start()
p = Pool(5)
ret_l = []
# for i in range(10):
# ret = p.apply_async(get,args=(i,))
# ret_l.append(ret)
# for ret in ret_l:
# content = ret.get()
# print(len(content))
for i in range(10):
p.apply_async(get,args=(i,),callback=call_back)
p.close()
p.join() # 将n个任务交给n个进程去执行
# 每一个进程在执行完毕之后会有一个返回值,这个返回值会直接交给callback参数指定的那个函数去进行处理
# 这样的话 所有的进程 哪一个执行的最快,哪一个就可以先进性统计工作
# 能在最短的时间内得到结果

day 27 Python中进程的操作的更多相关文章

  1. Python中的字符串操作总结(Python3.6.1版本)

    Python中的字符串操作(Python3.6.1版本) (1)切片操作: str1="hello world!" str1[1:3] <=> 'el'(左闭右开:即是 ...

  2. Python中的切片操作

    python中的切片操作功能十分强大,通常我们利用切片来进行提取信息,进行相关的操作,下面就是一些切片的列子. 列如我们从range函数1-100中取7的倍数,函数及结果如下所示: >>& ...

  3. python中的赋值操作和复制操作

    之前一直写C#,变量之间赋值相当于拷贝,修改拷贝变量不会改变原来的值.但是在python中发现赋值操作本质是和C++中的引用类似,即指向同一块内存空间.下面通过一个例子说明: p=[0,1,2,3,4 ...

  4. python中的赋值操作

    参考:https://www.cnblogs.com/andywenzhi/p/7453374.html?tdsourcetag=s_pcqq_aiomsg(写的蛮好) python中的赋值操作“=” ...

  5. python中的日志操作和发送邮件

    1.python中的日志操作 安装log模块:pip install nnlog 参数:my_log = nnlog.Logger('server_log.log',level='debug',bac ...

  6. python中OS模块操作文件和目录

    在python中执行和操作目录和文件的操作是通过内置的python OS模块封装的函数实现的. 首先导入模块,并查看操作系统的类型: >>> import os os.name # ...

  7. Python中的json操作

    Python中的json操作 标签(空格分隔): python 编码 json 字符串前缀问题 字符串前缀可以有r,u r:表示原始(raw)字符串,比如'\n'不会被转义.常用于正则. u:表示un ...

  8. python中的句柄操作

    python中的句柄操作 制作人:全心全意 通过窗口标题获取句柄 import win32gui hld = win32gui.FindWindow(None,u"Adobe Acrobat ...

  9. python中序列的操作

    Python中的序列操作 可变对象:列表.字典.集合 不可变对象:数值.字符串.元组.forzenset 1.序列的通用操作 (1)测试元素是否存在 x in S和x not in S,返回True或 ...

随机推荐

  1. 数据库操作API 或万能的双下划线

    数据库操作API: 类型 描述 exact 精确匹配: polls.get_object(id__exact=14). iexact 忽略大小写的精确匹配: polls.objects.filter( ...

  2. mysql InnoDB引擎支持hash索引吗

    https://blog.csdn.net/doctor_who2004/article/details/77414742

  3. 优秀的 Go 存储开源项目和库

    可以看到,今年谷歌家的 Go 编程语言流行度有着惊人的上升趋势,其发展也是越来越好,因此本文整理了一些优秀的 Go 存储相关开源项目和库,一起分享,一起学习. 存储服务器(Storage Server ...

  4. 调用DATASNAP+FIREDAC的远程方法有时会执行二次SQL或存储过程的BUG(转永喃兄)

    调用DATASNAP+FIREDAC的远程方法有时会执行二次SQL或存储过程的BUG 1)查询会重复执行的情形:Result := DATASETPROVIDER.Data会触发它关联的DATASET ...

  5. 数据结构:Stack

    Stack设计与实现 Stack基本概念 栈是一种 特殊的线性表 栈仅能在线性表的一端进行操作 栈顶(Top):允许操作的一端 栈底(Bottom):不允许操作的一端 Stack的常用操作 创建栈 销 ...

  6. creator.d.ts 的错误

    //export class PhysicsCollider{ export class PhysicsCollider extends Collider{ ==================检查代 ...

  7. mycat的schema.xml的个人的一点理解

    官方文档里讲的详细的部分的我就不再赘述了,我只是谈谈我自己的理解 刚开始接触mycat,最重要的几个配置文件有server.xml,schema.xml,还有个rule.xml配置文件 具体都是干啥用 ...

  8. search() 方法解析

    search()方法支持正则表达式的String对象的方法. 好,我们直接来贴代码,看效果,从实践理解透析方法的知识点和实际运用. var str="Visit W3School!" ...

  9. 4.Java的流程控制.md

    目录 1. break语句 2.continue 3.foreach语句 4.可变参数: 1. break语句 Java的break语句,正常情况下,break和c++一样,但是Java可以设置一个标 ...

  10. css3的2D和3D的转换

    一:2D转换: 通过 CSS3  transform转换,我们能够对元素进行移动.缩放.转动.拉长或拉伸. 2D移动:translate().使用translate()函数,你可以把元素从原来的位置移 ...