Python 进程、线程、协程

本文详细介绍了Python中的进程和线程概念及其在爬虫开发中的应用,包括多进程的os.fork和multiprocessing模块实现,进程间的Queue和Pipe通信方式,以及多线程的创建和线程同步。此外,还探讨了全局解释器锁(GIL)的影响以及协程的优势,特别是使用gevent库实现的协程并发执行。
摘要由CSDN通过智能技术生成

进程和线程

在爬虫开发中,进程和线程的概念非常重要的,提高爬虫的工作效率,打造分布式爬虫,都离不开进程和线程的身影。本节将从多进程、多线程、协程三个方面,帮组大家回顾 Python 语言中进程和线程的常用操作,以便在接下来的爬虫开发中灵活运用进程和线程。

多进程

Python 实现多进程的方式主要有两种,一种方法是使用 os 模块中的 fork 方法,另一种方法是使用 multiprocessing 模块。这两种方法的区别在于前者仅使用 Unix/Linux 操作系统,对 Windows 不支持,后者则是跨平台的实现方式。由于现在很多爬虫程序都运行在 Unix/Linux 操作系统上,所以本节对两种方式进行讲解

  1. 使用 os 模块中的 fork 方式实现多进程

    Python 的 os 模块封装了常见的系统调用,其中就有 fork 方法。fork 方法来自Unix/Linux 操作系统中提供的一个 fork 系统调用,这个方法非常特殊。普通的方法都是调用一次返回一次,而 fork 方法是调用一次返回两次,原因在于操作系统将当前进程(父进程)复制出一份进程(子进程),这两个进程几乎完全相同,于是 fork 方法分别在父进程和子进程中返回。子进程中永远返回 0,副进程中返回的是子进程的 ID。下面举个例子,对Python使用 fork 方法创建进程进行讲解。其中 os 模块中的 getpid 方法用于获取当前进程 ID,getppid 方法获取父进程的 ID。代码如下:

    import os
    if __name__ == '__main__':
        print('current Process (%s) start ...' % (os.getpid()))
        pid = os.fork()
        if pid < 0:
            print('error in fork')
        elif pid == 0:
            print('I am child process(%s) and my parent process is (%s)', os.getpid(), os.getppid())
        else:
            print('I(%s) created a chlid process (%s).', (os.getpid(), pid))
     
    >>> 
    current Process (64764) start ...
    I(64763) created a chlid process (64765).
    I(64764) created a chlid process (64766).
    I am child process(64765) and my parent process is (64763)
    I am child process(64766) and my parent process is (64764)
    
  2. 使用 multiprocessing 模块创建多线程

​ multiprocessimg 模块提供一个 Process 类来描述一个进程对象。创建子进程时,只需要传入一个执行函数和函数的参数,即可完成一个 Process 实例的创建,用 start() 方法启动进程,用 join() 方法实现进程间的同步。下面通过一个例子来演示创建多进程的流程,代码如下:

import os
from multiprocessing import Process


def run_proc(name):
    print('Child process %s (%s) Running...' % (name, os.getpid()))


if __name__ == '__main__':
    print('parent process %s' % (os.getpid()))
    for i in range(5):
        p = Process(target=run_proc, args=str(i))
        print('Process will start')
        p.start()
    p.join()
    print('Process end.')

以上介绍了创建进程的两种方法,但是要启动大量的子进程,使用进程池批量创建子进程的方式更加常见,因为当被操作的对象数目不大时,可以直接利用 multiprocessing 中的 Process 动态生成多个进程,如果上百个、上千个目标,手动去限制进程数量却又太过频繁,这时候进程池 Pool 发挥作用的时候就到了。

  1. multiprocessing 模块提供了一个 Pool 类来代表进程池对象

    Pool 可以提供指定数量的进程提供用户调用,默认大小是 CPU 的核数。当有新的请求提交到 Pool 中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定的最大值,那么该请求就会等待,知道池中有进程结束,才会创建新的进程来处理它。下面通过一个例子来演示进程池的工作流程,代码如下:

    import os
    import time
    import random
    from multiprocessing import Pool
    
    
    def run_task(name):
        print('Task {} (pid={}) is running...'.format(name, os.getpid()))
        time.sleep(random.random() * 3)
        print('Task {} end.'.format(name))
    
    
    if __name__ == '__main__':
        print('Current process {}'.format(os.getpid()))
        p = Pool(processes=3)
        for i in range(5):
            p.apply_async(run_task, args=(i, ))
        print('Waiting for all subprocesses done...')
        p.close()
        p.join()
        print('All processes done...')
    
        
    >>>>
    Current process 70694
    Waiting for all subprocesses done...
    Task 0 (pid=70696) is running...
    Task 1 (pid=70697) is running...
    Task 2 (pid=70698) is running..
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值