进程池

为什么会有进程池的概念:
每次开启一个进程就要为其设立一个内存空间,这个需要设立内存空间需要时间;
其次进程过多,操作系统要不断在不同的进程之间进行调度,这个也需要时间。
所以进程的个数不能无限制增加。
这就需要涉及到进程池的概念。

进程池:
python中的进程池,先创建一个属于进程的池子;
这个池子指定能存放多个进程;
比如现在设立一个进程池:这个进程池中有5个个进程;当外部有50个任务需要执行的时候,首先这个5个任务对应五个进程开始执行,当每一个任务执行结束的时候,这个进程截至执行下一个任务,中间不会关闭,直至这50个任务最终执行完毕。
好处:相比50个任务同时执行,创建50个进程节省了内存空间,节省了50个进程创建所需要的时间,节省了操作系统在调度50个进程时所需要花费的时间;相比50个任务逐个执行,节省了时间。

信号量:是200个任务,产生200个进程,但是同一时间只能执行5个进程;

 

更高级的进程池:
进程池的个数有上限和下限
比如博客园这个网站,在白天上班时间访问的人多,晚上12点至凌晨6点访问的人少,也就是说访问量随着时间的变化有个波动。那么当访问少的时候,用进程池数量的下限,访问量大的时候用上限。这样能够减少操作系统的负担。
一般每个电脑的进程数量是处理器核数加1,比如四核的处理器最多是5个进程。
但是python里面没有这个高级进程池

经验:当处理需要5个进程以上的时候,就开启进程池

 

from multiprocessing import Pool
def func(n):
    for i in range(10):
        print(n+1)

if __name__=='__main__':
    pool=Pool(5)#进程池中设置五个进程
    pool.map(func,range(100))#外部总共有100个进程

进阶,比较时间的差异

import time
from multiprocessing import Pool,Process
def func(n):
    for i in range(10):
        print(n+1)

if __name__=='__main__':
    start1=time.time()
    pool=Pool(5)#进程池中设置五个进程
    pool.map(func,range(100))#外部总共有100个进程
    t1=time.time()-start1

    #下面是用进程方法重新做一遍,来比较以下速度的差异
    start2 = time.time()
    p_list=[]
    for i in range(100):
        p=Process(target=func,args=(i,))
        p_list.append(p)
        p.start()
    for p in p_list:
        p.join()
    t2 = time.time() - start2
    print(t1,t2)

得到t1=0.41053128242492676, t2=8.958720684051514

使用进程池,相比不使用进程池在时间上减少了20倍。

进程池的几种方法:

import time
import os
from multiprocessing import Pool,Process
def func(n):
    print('start func%s'%n,os.getppid())
    time.sleep(1)
    print('end func%s' % n, os.getppid())

if __name__=='__main__':
    p=Pool(5)
    for i in range(4):
        p.apply(func,args=(i,))


D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/sever.py
start func0 9980
end func0 9980
start func1 9980
end func1 9980
start func2 9980
end func2 9980
start func3 9980
end func3 9980

Process finished with exit code 0

从以上结果可知,在执行过程中是start和end是同步执行的,那么这样想,如果是同步执行的话,那么进程池就失去了其原本的意义,那么如何实现异步执行。

import time
import os
from multiprocessing import Pool,Process
def func(n):
    print('start func%s'%n,os.getppid())
    time.sleep(1)
    print('end func%s' % n, os.getppid())

if __name__=='__main__':
    p=Pool(5)
    for i in range(8):
        p.apply_async(func,args=(i,))#实现进程池的异步执行
    p.close()#结束进程池接收任务,阻止进程池中的进程再继续接收新的任务
    p.join()#感知进程池中任务执行结束,因为进程池中的进程会在执行完一批任务后接着执行下一批任务,所以单纯使用join不能达到预期效果,必须在前面加上close,

用进程池实现socket聊天的效果

sever

import socket
sk=socket.socket()

sk.connect(('127.0.0.1',8080))

msg=sk.recv(1024).decode('utf-8')
print(msg)
msg1=input('>>>:').encode('utf-8')
sk.send(msg1)

sk.close()

这里代码自己感觉没有问题:但是运行后总是报错ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。以前从没有出现过,以后解决

clent端

import socket
from multiprocessing import Pool
def func():
    conn.send(b'hello')
    print(conn.recv(1024).decode('utf-8'))
    conn.close()

if __name__=='__main__':
    p=Pool(5)
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    while 1:
        conn,addr=sk.accept()
        p.apply_async(func,args=(conn,))
    sk.close()
#实现接收和发送的异步

例子

from multiprocessing import Pool
import time

def func(i):
    time.sleep(0.5)
    return i*i

if __name__=='__main__':
    p=Pool(5)
    for i in range(5):
        res=p.apply(func,args=(i,))#apply的结果就是func的返回值
        print(res)

上面是appy的同步执行,在执行结果上,执行的结果是逐个被执行出来的

那么异步执行:

from multiprocessing import Pool
import time

def func(i):
    time.sleep(0.5)
    return i*i

if __name__=='__main__':
    p=Pool(5)
    for i in range(10):
        res=p.apply_async(func,args=(i,))#apply的结果就是func的返回值
        print(res.get())

在执行结果上,所有的执行结果还是一个个逐个出现,没有体现出异步执行应有的效果。其本质是计算的过程中使用了进程池,很快便执行完成;但是在res.get()时候发生了阻塞,所以导致结果是一个个逐渐出来,这时候就要将所有的进程放在列表中去执行。

from multiprocessing import Pool
import time

def func(i):
    time.sleep(0.5)
    return i*i

if __name__=='__main__':
    p=Pool(5)
    res_list=[]
    for i in range(10):
        res=p.apply_async(func,args=(i,))#apply的结果就是func的返回值
        res_list.append(res)
    for res in res_list:
        print(res.get())

使用map,

from multiprocessing import Pool
import time

def func(i):
    time.sleep(0.5)
    return i*i

if __name__=='__main__':
    p=Pool(5)
    ret=p.map(func,range(10))
    print(ret)

map和async的区别:
map是全部计算完以后再执行,
async是按照进程池中的进程数,分批执行,执行完一批输出一批。
map可以看作async的另一种形式,帮助我们简化了大量的操作。但是未来在实际使用中建议优先使用async

回调函数

from multiprocessing import Pool
import os

def func1(n):
    print('in func1:',os.getppid())
    return n*n


def func2(nn):
    print('in func2:', os.getppid())
    print(nn)

if __name__=='__main__':
    print('主进程:', os.getppid())
    p=Pool(5)
    p.apply_async(func1,args=(10,),callback=func2)
    p.close()
    p.join()

D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/sever.py
主进程: 3432
in func1: 6156
in func2: 3432
100

Process finished with exit code 0

在上述代码中func2是回调函数,其是在主进程中被执行的。

且根据执行结果可知:func2执行的结果是func1执行的结果。

 

回调函数是在主进程中执行的
回调函数主要在爬虫中使用,用在爬虫的处理数据过程中。

例子:

import requests
from multiprocessing import Pool

def func1(url):
    response=requests.get(url)
    if response.status_code==200:
        return url,response.content.decode('utf-8')

def func2(args):
    url,content=args
    print(url,len(content))

if __name__=='__main__':
    url_list=[
        'https://www.cnblogs.com/',
        'http://www.baidu.com',
        'https://www.sogou.com/',
        'http://www.sohu.com/',
    ]
    p=Pool(5)
    for url in url_list:
        p.apply_async(func1,args=(url,),callback=func2)
    p.close()
    p.join()

在上面的例子中,主进程只是派发出去了url_list后没有其他任务,那么这样做不利于计算机工作效率的提升。将回调进程在主进程中执行,有助于提升计算机的效率。

 

posted @ 2019-04-13 22:26  舒畅123  阅读(138)  评论(0编辑  收藏  举报