可以实现异步的操作,进程,线程,协程

一.进程的实现

  1. 进程创建之后一定最少有一个线程,这个线程是用来去执行代码的
  2. 操作系统在开启进程时会随机分配一个进程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('主进程执行结束!')

结果:




python获取子进程 python 主进程 子进程_子进程


主进程创建的子进程是异步执行的,那么我们就验证一下,并且看一下子进程和主进程来看看是否是父子关系。

  • 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('主进程执行结束!')

结果:


python获取子进程 python 主进程 子进程_子进程_02


  • 如何手动给注册在子线程中的函数传递指定的参数?
  • 使用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('主进程执行结束!')

结果:


python获取子进程 python 主进程 子进程_子进程_03


使用进程实现异步效果:

  • 同步效果:
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)

结果:


python获取子进程 python 主进程 子进程_python获取子进程_04


  • 异步效果
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('主进程执行结束!')
    #发现:主进程执行结束后,子进程才执行结束!

结果:


python获取子进程 python 主进程 子进程_Powered by 金山文档_05


  • 加上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('主进程执行结束!')

结果:


python获取子进程 python 主进程 子进程_python_06


还有循环的情况:

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)

结果:


python获取子进程 python 主进程 子进程_python获取子进程_07


守护线程(了解)

代码:

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个子进程胡乱执行


python获取子进程 python 主进程 子进程_子进程_08


加锁之后:

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()

结果:


python获取子进程 python 主进程 子进程_python获取子进程_09


解析:创建了一个进程锁,传给了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...')