多进程的一些基本用法

前言

python调用多进程还是非常方便快捷的,他有一个专门的包(multiprocessing)。借助它,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步处理,它提供了一些主要的用法,比如Pool,Queue,Lock等,我们一起去看下他的一些基本用法吧。

常用用法

multiprocessing这个包的基本用法还是非常简单的,让我们看下他的一个小例子

import multiprocessing

def process(num):
    print('Process:',num)
if __name__ == '__main__':
    for i in range(5):
        ## args表示被调用对象的位置参数元组,比如target是函数a,他有两个参数m,n,那么args就传入(m, n)即可
        p = multiprocessing.Process(target=process, args=(i,))   # target为调用的函数,args中传入输入的值
        p.start()   # 使用start() 去掉用进程

看下其输出:

Process: 3
Process: 1
Process: 0
Process: 4
Process: 2

这个就是多进程一个非常简单的用法了。我们在平常书写代码时,为了代码的整洁和工整性,往往会使用类。没关系,他也可以继承Process类,自定义个进程类。话不多说,用例子感受一下:

from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self, loop):
        # 因为Process类本身也有__init__方法,这个子类相当于重写了这个方法,
        # 但这样就会带来一个问题,我们并没有完全的初始化一个Process类,所以就不能使用从这个类继承的一些方法和属性,
        # 最好的方法就是将继承类本身传递给Process.__init__方法,完成这些初始化操作
        Process.__init__(self)   #重写父类
        self.loop = loop

    def run(self):
        for count in range(self.loop):
            time.sleep(1)
            print('Pid: ' + str(self.pid) + ' LoopCount: ' + str(count))


if __name__ == '__main__':
    for i in range(2, 5):
        p = MyProcess(i)  # 子进程
        ### 当父进程结束后,子进程会自动被终止。
        # p.daemon = True
        p.start()
        # p.join()  # 加入join()以后,会执行到结束再关闭所有子进程
    	##在这一步骤的时候,执行完print语句后,主进程结束,所以下面的子进程也会自动关闭,可以防止无控制生成子进程
    print("结束了QAQ")  # 父进程

可以看到,上面有个属性叫daemon,他还是非常好用的,给他传入的是一个bool值,当我们设置他为True的时候,当父进程结束后,子进程会自动被终止。我们看下输出:

结束了QAQ
Process finished with exit code 0

我们发现,他只会打印一个字符串,就和他定义了一样,父进程结束后,所有的子进程也都关闭。但是这个就带来一个问题,我们正常的需求就是在父进程执行完后,他的子进程也希望执行完再关闭。别担心,它还有一个属性叫做join()用法,他的存在完全会实现我们之前的需求。加入join()方法以后,我们看下其输出:

Pid: 6308 LoopCount: 0
Pid: 6308 LoopCount: 1
Pid: 1964 LoopCount: 0
Pid: 1964 LoopCount: 1
Pid: 1964 LoopCount: 2
Pid: 5384 LoopCount: 0
Pid: 5384 LoopCount: 1
Pid: 5384 LoopCount: 2
Pid: 5384 LoopCount: 3
结束了QAQ

Process finished with exit code 0

是不是和我们的预期期望是一样的。上面就是多进程继承类的一些基本用法。
当我们仔细观察上面输出结果,我们就会发现他的一些进程发生了错位运行。这是为什么呢?其实不难理解。这是由于并行导致的,两个进程同时进行了输出,结果第一个进程的换行没有来得及输出,第二个进程就输出了结果。
那么有了问题,就想把它给解决,如何让进程之间有序的运行呢,这边就又有了一个概念叫做进程锁-Lock。
我们可以通过 Lock 来实现,在一个进程输出时,加锁,其他进程等待。等此进程执行结束后,释放锁,其他进程可以进行输出。让我们来看下他的用法吧:

from multiprocessing import Process
from multiprocessing import Lock
import time
## 加入进程锁这个概念,防止进程在运行过程中发生错位
##在我们并没有打开进程锁这个过程中,我们发现有的进程已经发生了错位,其实可以看出,让每一个进程有序的执行是很有必要的
class MyProcess(Process):
    def __init__(self, loop,lock):
        Process.__init__(self)
        self.loop = loop
        self.lock = lock
    def run(self):
        ## 进程锁需要在此进程执行之前加锁
        for count in range(self.loop):
            time.sleep(0.1)
            self.lock.acquire()  # 加锁
            print('Pid: ' + str(self.pid) + ' LoopCount: ' + str(count))
            self.lock.release()  # 释放锁

if __name__ == '__main__':
    lock = Lock()
    for i in range(10, 15):
        p = MyProcess(i,lock)
        p.start()

在上面的代码中,我们给print输出加入了锁机制,调用了acquire()以及release()两个方法,这样可以实现进程之间的有序进行。看下输出:

Pid: 8524 LoopCount: 0
Pid: 8460 LoopCount: 0
Pid: 8300 LoopCount: 0
Pid: 8444 LoopCount: 0
Pid: 8504 LoopCount: 0
Pid: 8460 LoopCount: 1
Pid: 8524 LoopCount: 1
Pid: 8300 LoopCount: 1
Pid: 8444 LoopCount: 1
Pid: 8504 LoopCount: 1
Pid: 8460 LoopCount: 2
Pid: 8524 LoopCount: 2
Pid: 8300 LoopCount: 2
Pid: 8444 LoopCount: 2
Pid: 8504 LoopCount: 2
Pid: 8460 LoopCount: 3
Pid: 8524 LoopCount: 3
Pid: 8300 LoopCount: 3
Pid: 8444 LoopCount: 3
Pid: 8504 LoopCount: 3
Pid: 8524 LoopCount: 4
Pid: 8460 LoopCount: 4
Pid: 8300 LoopCount: 4
Pid: 8444 LoopCount: 4
Pid: 8504 LoopCount: 4
Pid: 8524 LoopCount: 5
Pid: 8460 LoopCount: 5
Pid: 8300 LoopCount: 5
Pid: 8444 LoopCount: 5
Pid: 8504 LoopCount: 5
Pid: 8524 LoopCount: 6
Pid: 8460 LoopCount: 6
Pid: 8300 LoopCount: 6
Pid: 8444 LoopCount: 6
Pid: 8504 LoopCount: 6
Pid: 8524 LoopCount: 7
Pid: 8460 LoopCount: 7
Pid: 8300 LoopCount: 7
Pid: 8444 LoopCount: 7
Pid: 8504 LoopCount: 7
Pid: 8460 LoopCount: 8
Pid: 8524 LoopCount: 8
Pid: 8300 LoopCount: 8
Pid: 8444 LoopCount: 8
Pid: 8504 LoopCount: 8
Pid: 8460 LoopCount: 9
Pid: 8524 LoopCount: 9
Pid: 8300 LoopCount: 9
Pid: 8444 LoopCount: 9
Pid: 8504 LoopCount: 9
Pid: 8524 LoopCount: 10
Pid: 8460 LoopCount: 10
Pid: 8444 LoopCount: 10
Pid: 8504 LoopCount: 10
Pid: 8460 LoopCount: 11
Pid: 8444 LoopCount: 11
Pid: 8504 LoopCount: 11
Pid: 8460 LoopCount: 12
Pid: 8444 LoopCount: 12
Pid: 8444 LoopCount: 13

Process finished with exit code 0

可以看到,上面的进程之间有序运行了。
多进程在一定程度上能够提升爬虫的运行的效率,但是一旦网页网址请求数量过多的话,进程无限开启,这样会占用大量地资源,导致机器卡顿。这边要引入一个进程池的概念,顾名思义,就是我们把大概需要用的进程设置一个临界值,小于这个的往内输入进程,一旦等于他,就等进程池内的进程释放,再往内输入。这样子就形成了一个循环。

from multiprocessing import Pool
import requests
from requests.exceptions import ConnectionError

def scrape(url):
    try:
        print(requests.get(url))
    except ConnectionError:
        print('Error Occured ', url)
    finally:
        print('URL ', url, ' Scraped')


if __name__ == '__main__':
    pool = Pool(processes=2)  # 设置了进程池内最大容量为两个
    urls = [
        'http://xxxyxxx.net',
        'http://www.meituan.com/',
        'http://blog.csdn.net/',
        'http://douban.com'
    ]  # 要测试的网址
    pool.map(scrape, urls)  # 可以通过map函数调用进程,异步请求

我们来看下输出:

<Response [200]>
URL  http://www.meituan.com/  Scraped
<Response [200]>
URL  http://blog.csdn.net/  Scraped
<Response [200]>
URL  http://douban.com  Scraped
Error Occured  http://xxxyxxx.net
URL  http://xxxyxxx.net  Scraped

Process finished with exit code 0

我们看到已经实现了代码块的请求,实现了异步请求。效率还是非常高的。
以上呢就是多进程在python内实现的一些基本用法。欢迎大家讨论,也希望本文能够对大家有所帮助,下面一篇文章内我将会使用多进程去实战爬取一个网站,给大家一个更加直观的感受。同时欢迎访问个人博客主页… …

本文参考:崔大大的多进程博客

发布了15 篇原创文章 · 获赞 9 · 访问量 8556

猜你喜欢

转载自blog.csdn.net/Since_you/article/details/89375400