可以实现异步的操作,进程,线程,协程
一.进程的实现
- 进程创建之后一定最少有一个线程,这个线程是用来去执行代码的
- 操作系统在开启进程时会随机分配一个进程id,区分当前运行的任务的
multprocessing包
multiprocess是python中管理进程的包。 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块,提供的子模块非常多。
Process模块
Process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。
运行一个py文件就相当于启动了一个进程,这个进程我们成为"主进程"
在主进程对应的py文件中,可以通过Process模块创建另一个进程,这个进程是基于主进程创建的,因此可以被称为"子进程"
当有了两个进程后,我们其实就可以实现异步机制了!
具体实现过程:
1.导入模块:from multiprocessing import Process
2.基于Process创建一个子进程对象(当前运行的整个py文件表示主进程),然后可以基于target参数将外部的一个函数注册到该子进程中
3.基于start()方法启动创建好的子进程
from multiprocessing import Process
def func():
print('我是绑定给子进程的一组任务!')
if __name__ == '__main__':
print('主进程开始执行!')
#用process模块创建一个进程p,给该进程绑定一组任务,就是函数
p = Process(target=func)
#启动创建好的进程
p.start()
print('主进程执行结束!')
结果:
主进程创建的子进程是异步执行的,那么我们就验证一下,并且看一下子进程和主进程来看看是否是父子关系。
- os.getpid() 获取自己进程的ID号
- os.getppid() 获取自己进程的父进程的ID号
import os
from multiprocessing import Process
from time import sleep
def func():
print('我是子进程!')
sleep(1)
print('子进程ID号:',os.getpid())
print('该子进程的父进程ID号:',os.getppid())
if __name__ == '__main__':
print('主进程开始执行!主进程的ID号:',os.getpid())
#创建一个进程p,给该进程绑定一组任务
p = Process(target=func)
#启动创建好的进程
p.start()
print('主进程执行结束!')
结果:
- 如何手动给注册在子线程中的函数传递指定的参数?
- 使用args传递,不能修改
from multiprocessing import Process
def func(num1,num2):
print('我是绑定给子进程的一组任务!',num1,num2)
if __name__ == '__main__':
print('主进程开始执行!')
#创建一个进程p,给该进程绑定一组任务
p = Process(target=func,args=(123,456))
#启动创建好的进程
p.start()
print('主进程执行结束!')
结果:
使用进程实现异步效果:
- 同步效果:
import time
def get_request(url):
print('正在请求网址的数据:',url)
time.sleep(2)
print('请求结束:',url)
if __name__ == "__main__":
start = time.time()
urls = ['www.1.com','www.2.com','www.3.com']
for url in urls:
get_request(url)
print('总耗时:',time.time()-start)
结果:
- 异步效果
import time
from multiprocessing import Process
def get_request(url):
print('正在请求网址的数据:',url)
time.sleep(2)
print('请求结束:',url)
if __name__ == "__main__":
urls = ['www.1.com','www.2.com','www.3.com']
for url in urls:
#创建了三个子进程,表示三组任务。每个子进程执行单独操作,cpu交替执行,无序
p = Process(target=get_request,args=(url,))
p.start()
join方法的使用:主进程会在加上join的地方等待(也就是阻塞住),会等待子进程执行完之后,再继续往后执行主进程join后序的部分
- 没有join的情况:
from multiprocessing import Process
import time
def func():
print('子进程正在执行......')
time.sleep(2)
print('子进程执行结束!')
if __name__ == '__main__':
print('主进程正在执行......')
p = Process(target=func)
p.start()
print('主进程执行结束!')
#发现:主进程执行结束后,子进程才执行结束!
结果:
- 加上join的情况:
from multiprocessing import Process
import time
def func():
print('子进程正在执行......')
time.sleep(2)
print('子进程执行结束!')
if __name__ == '__main__':
print('主进程正在执行......')
p = Process(target=func)
p.start()
p.join()
print('主进程执行结束!')
结果:
还有循环的情况:
import time
from multiprocessing import Process
def get_request(url):
print('正在请求网址的数据:',url)
time.sleep(2)
print('请求结束:',url)
if __name__ == "__main__":
start = time.time()
urls = ['www.1.com','www.2.com','www.3.com']
ps = [] #存储创建好的多个子进程
for url in urls: #三个子进程创建且启动了,将三个子进程存储到了ps列表中
#创建了三个进程,表示三组任务
p = Process(target=get_request,args=(url,))
p.start()
ps.append(p)
for p in ps: #p就是ps列表中的子进程
p.join() #相当于每个子进程都执行了join操作
print('总耗时:',time.time()-start)
结果:
守护线程(了解)
代码:
import time
from multiprocessing import Process
def get_request(url):
print('正在请求网址的数据:',url)
time.sleep(2)
print('请求结束:',url)
if __name__ == "__main__":
start = time.time()
p = Process(target=get_request,args=('www.1.com',))
# 将当前p这个子进程设置为了守护进程
p.daemon = True #该操作必须放置在子进程启动操作之前
p.start()
print('主进程执行结束')
p.daemon = True,加上这个,就表示主进程结束,不管子进程有没有执行完都要结束。
进程同步(锁)
注意:进程的创建的顺序和执行的顺序是不同的,是操作系统决定的。所以是乱序的
如何解决:进程锁
加锁流程:
1.导包:from multiprocessing import Lock
2.加锁:lock.acquire()
3.解锁:lock.release()
没有枷锁:
#在当前目录下创建一个文件(db)
#文件db的内容:{"count":1}表示的是余票数量
from multiprocessing import Process
import time,json,random
def search():#查询db文件中的余票数量
fp = open('./db.txt','r')
dic = json.load(fp) #反序列化,将文件中的json数据转成python字典对象
print('剩余车票数量为:',dic['count'])
def get(): #负责抢票,一次只能购买一张票
fp = open('./db.txt', 'r')
dic = json.load(fp)
time.sleep(0.1)
if dic['count'] > 0:#还有剩余车票
time.sleep(0.2)
dic['count'] -= 1 #一次只能购买一张票
time.sleep(0.1)
#购买车票后,余票数量发生了变化,将最新的余票数量在写回到db文件中进行存储
json.dump(dic,open('./db.txt','w'))
print('购票成功!')
def task():
search() #先查询
get() #后购买
if __name__ == '__main__':
for i in range(3):#创建三个子进程
p = Process(target=task)
p.start()
结果:胡乱执行。3个子进程胡乱执行
加锁之后:
from multiprocessing import Process
import time,json,random
from multiprocessing import Lock #进程锁
def search():#查询db文件中的余票数量
fp = open('./db.txt','r')
dic = json.load(fp) #反序列化,将文件中的json数据转成python字典对象
print('剩余车票数量为:',dic['count'])
def get(): #负责抢票,一次只能购买一张票
fp = open('./db.txt', 'r')
dic = json.load(fp)
time.sleep(0.1)
if dic['count'] > 0:#还有剩余车票
time.sleep(0.2)
dic['count'] -= 1 #一次只能购买一张票
time.sleep(0.1)
#购买车票后,余票数量发生了变化,将最新的余票数量在写回到db文件中进行存储
json.dump(dic,open('./db.txt','w'))
print('购票成功!')
def task(lock):
lock.acquire() #上锁
search() #先查询
get() #后购买
lock.release() #解锁
if __name__ == '__main__':
lock = Lock() #创建了一把进程锁
for i in range(3):#创建三个子进程
p = Process(target=task,args=(lock,))
p.start()
结果:
解析:创建了一个进程锁,传给了task函数上锁,可以让三个子进程一个一个执行。
进程池得使用
import os
import time
import random
from multiprocessing import Pool
def worker(msg):
# 程序启动的时间
t_start = time.time()
print('%s 开始执行, 进程号: %d' % (msg, os.getpid()))
time.sleep(random.random() * 2)
# 程序结束时间
t_stop = time.time()
print(msg, '执行完毕, 耗时: %0.2f' % (t_stop - t_start))
print('-' * 50)
# 进程池的使用
if __name__ == '__main__':
po = Pool(3) #开启三个进程池
for i in range(10):
# 调用进程池
po.apply(worker, (i,)),每次分配三个子进程给池子里
print('start...')
# 关闭进程池
po.close()
# 已经在进程池中的任务要执行完毕才能在主进程中往下执行代码
po.join()
print('end...')