Python爬虫:线程,进程与协程

以往的爬虫我们都采用单线程和同步的方式,这导致我们的爬虫及其脆弱,因为一点报错都会让它停下来,而且面对比较大的数据,爬虫只能选择等待,这种阻塞会消耗很多时间,为什么我们不把等待的这些时间去干别的事呢?

线程与进程

线程和进程是相似的 

一,概念梳理

线程: 程序内,可以直接被CPU调用的执行过程,是操作系统能够进行运算的最小单位,它被包含在进程中实际运作的单位。

进程:运行中的程序,每次我们执行应该程序,操作系统会自动地为这个程序准备一些必要的资源(分配内存,创造一个能执行的线程)

形象来说,线程就像是员工,进程就是公司,线程组成进程。如果我们想要提升效率,我们可以多招些员工(多线程)或者开一些分公司,连锁(多进程) 

二,代码实现

多线程

from threading import Thread
# def func(name):
#     for i in range(10):
#         print(name,i)

 #创建任务
def func_1(name):
    for i in range(100):
        print(name,i)


if __name__ == '__main__':
    # func("A")
    # func("B")
    # func("C")
    # #创建线程
    t1 = Thread(target=func_1,args=("A",))
    t2 = Thread(target=func_1,args=("B",))
    t3 = Thread(target=func_1,args=("C",))
    t1.start()
    t2.start()
    t3.start()

实例化线程对象:Thread(taeget=目标函数,不要括号,args=()填参数,元组形式) 

开启线程:线程对象的start方法

面向对象写法 

from threading import Thread
class MyThread(Thread):
    def __init__(self,name):
        super(MyThread, self).__init__()
        self.name = name
    def run(self):
        for i in range(100):
            print(self.name,i)

if __name__ == '__main__':
    t1 = MyThread("A")
    t2 = MyThread("B")
    t3 = MyThread("C")
    t1.start()
    t2.start()
    t3.start()

需要注意的是,函数执行的任务必须重写到run方法中,其他的一样。

很快,一个一个实例化线程对象的方法无法满足我们了,如果我们要申请20个线程呢?这太繁琐了

 线程池的产生就是顺其自然的了

线程池

from concurrent.futures import ThreadPoolExecutor

def func(name):
    for i in range(50):
        print(name,i)

if __name__ == '__main__':
    with ThreadPoolExecutor(10) as t:
        t.submit(func,"A")
        t.submit(func,"B")
        t.submit(func,"C")

这大大地减轻了我们的工作量,我们只需要把任务丢进线程池里,它就会自动分配线程为我们处理,当然,最大线程数我们可以设置,上例使用了10个。 

好了,上面的所有情况我们都忽视掉了函数有返回值的情况是,那么,这种情况该怎么处理呢?

from concurrent.futures import ThreadPoolExecutor

def func(name):
    print(name)
    return name

def fun(res):
    print(res.result())

if __name__ == '__main__':
    with ThreadPoolExecutor(3) as t:
        t.submit(func,"A").add_done_callback(fun)
        t.submit(func,"B").add_done_callback(fun)
        t.submit(func,"C").add_done_callback(fun)

或者

from concurrent.futures import ThreadPoolExecutor

def func(name):
    print(name)
    return name

def fun(res):
    print(res.result())

if __name__ == '__main__':
    with ThreadPoolExecutor(3) as t:
         results = t.map(func,["A","B","C"])
         for result in results:
            print(result)

 这样我们就可以获取函数返回值了。

多进程

from multiprocessing import Process
from concurrent.futures import ProcessPoolExecutor
def func(name):
    for i in range(100):
        print(name,i)

if __name__ == '__main__':
    p1 = Process(target=func,args=("A",))
    p2 = Process(target=func,args=("B",))
    p3 = Process(target=func,args=("C",))
    p1.start()
    p2.start()
    p3.start()

 怎么样,对比一下多线程,你会发现代码上除了调的类不一样以外,就没区别了,多进程的进程池和线程池几乎一模一样,这里就不赘述了,可以自己尝试一下,进程池上面代码有名字~

线程和进程是相似的 

那么,多进程和多线程的应用场景有什么不同呢?

多线程:任务相对统一,互相特别相似

多进程:多个任务相互独立,很少有交集

多任务异步协程

协程:协程是一种比函数更加强大的控制流结构,它可以挂起(暂停)自身的执行并在稍后从上次挂起的地方恢复执行。协程允许在单个线程内进行非阻塞的协作式多任务处理,这意味着程序可以在等待某个耗时操作(如I/O操作、网络请求)完成的同时,去做其他事情,从而提高整体效率。协程拥有自己的执行上下文(包括局部变量和指令指针),可以在多个协程之间方便地切换。

简单理解,多线程可以理解为我们不停的切换cpu运算对象,任务不动,CPU动。而协程是单线程中,通过移动任务来实现的,比如任务1进入这条线程中,进行计算.....直到产生IO阻塞,任务1就出线程,任务2进入线程,以此类推,当任务1IO阻塞结束,再让它回到线程(如果还有必要的话)。这个过程实质上是CPU不动,任务移动。这其实效率更高,因为CPU调用切换是需要消耗内存的,它是属于系统层面的问题,而协程只需要切换任务,这是代码层面的问题,实际上是不需要多少时间和空间的。

异步协程代码实现

import asyncio

async def func():
    print("我是函数")

if __name__ == '__main__':
    #协程对象想要执行,必须借助于 event_loop
    f = func()
    event_loop = asyncio.get_event_loop()
    event_loop.run_until_complete(f)#eventloop运行协程对象,直到该对象内的内容执行完毕为止

 这是协程的基本结构,async表示异步,因为我们需要IO阻塞,想象一下如果我们申请网页响应中不执行IO阻塞,那在申请的同时代码就已经跑到下面去了,如果我们下面需要这个网页的源码呢?这就必然报错了,所以这就是为什么我们的函数使用了异步操作,本质还是在IO阻塞上。


import asyncio

async def func1():
    print("我是func1")
    await asyncio.sleep(1)
    print("func1结束")

async def func2():
    print("我是func2")
    await asyncio.sleep(2)
    print("func2结束")

async def func3():
    print("我是func3")
    await asyncio.sleep(3)
    print("func3结束")

if __name__ == '__main__':
    f1 = func1()
    f2 = func2()
    f3 = func3()

    tasks = [
        f1,f2,f3
    ]
    asyncio.run(asyncio.wait(tasks))

上例我们创建了三个任务,并将这三个任务添加到了协程中,wait()为何意?这是为了让我们三个任务必须执行完才关闭eventloop,要不然还有任务在io阻塞,协程直接关了怎么办。

异步协程的函数返回值

import asyncio
async def func1():
    print("我是func1")
    await asyncio.sleep(1)
    print("func1结束")
    return "func1的返回值"

async def func2():
    print("我是func2")
    await asyncio.sleep(2)
    print("func2结束")
    return "func2的返回值"

async def func3():
    print("我是func3")
    await asyncio.sleep(3)
    print("func3结束")
    return "func3的返回值"

async def main():
    f1 = func1()
    f2 = func2()
    f3 = func3()
    tasks = [
        asyncio.create_task(f1),
        asyncio.create_task(f2),
        asyncio.create_task(f3)
    ]
    # done,padding = await asyncio.wait(tasks)
    # for res in done:
    #     print(res.result())
    # gather和wait区别:gather返回值是有顺序的(按照你添加任务的顺序)
    result = await asyncio.gather(*tasks,return_exceptions=False)#return exception=true表示如果任务中有错误信息,则返回错误信息,其他任务正常执行。
    print(result)
if __name__ == '__main__':
    asyncio.run(main())

 解释请看代码注释~

创作不易,点个小赞鼓励一下呗~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/591455.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TCP的特性(4)

TCP特性 拥塞控制(可靠性机制)延迟应答(效率机制)捎带应答(效率机制)面向字节流(粘包问题)TCP异常机制小结 拥塞控制(可靠性机制) 虽然TCP引入了滑动窗口,能够高效可靠的传输大量数据,但是在开始阶段就发送大量数据,可能引起一系列问题. TCP引入了慢启动机制,先发少量的数据,判…

PDF Shaper Ultimate 免安装中文破姐版 v14.1

软件介绍 PDF Shaper是一套完整的多功能PDF编辑工具,可实现最高的生产力和文档安全性。它允许你分割,合并,水印,署名,优化,转换,加密和解密您的PDF文件,也可插入和移动页&#xff0…

RabbitMQ知识点总结和复习

之前项目中用到RabbitMQ的场景主要是订单信息的传递,还有就是利用RabbitMQ的死信队列属性设置,实现延迟队列效果,实现超时支付取消功能,以及在两个不同项目中传递数据等场景。 最近几年的工作中都是一直用的RabbitMQ,…

【C++】深入剖析C++11 initializer_list 新的类功能 可变模板参数

目录 一、std::initializer_list 1、std::initializer_list是什么类型 2、std::initializer_list 的应用场景 ①给自定义容器赋值 ② 传递同类型的数据集合 二、新的类功能 1、默认成员函数 2、关键字default 3、关键字delete 三、可变参数模板 一、std::initialize…

关于Linux的“三十年河东,三十年河西”的思考

目录 一、何为“三十年河东,三十年河西”? 二、Linux系统的发展历程简介 三、Linux家族 四、Linux发展分支 五、关于对Linux发展的回顾 一、何为“三十年河东,三十年河西”? “三十年河东,三十年河西”原义是三十…

OpenWRT有线桥接部署教程

前言 之前咱们讲到OpenWRT部署WAN实现PPPoE拨号上网和自动获取IP模式上网的办法: OpenWRT设置PPPoE拨号教程 OpenWRT设置自动获取IP,作为二级路由器 这一次,咱们尝试用OpenWRT有线桥接上一级路由器的教程。 可能有小伙伴敏锐地发现了&am…

20232803 2023-2024-2 《网络攻防实践》实践八报告

目录 1. 实践内容2. 实践过程2.1 动手实践任务一2.2 动手实践任务二:分析Crackme程序2.2.1 crackme1.exe2.2.2 crackme2.exe 2.3 分析实践任务一2.4 分析实践任务二 3. 学习中遇到的问题及解决4. 学习感悟、思考等 1. 实践内容 动手实践任务一:对提供的r…

【Python编程实践1/3】模块

目录 目标 模块 import ​编辑 代码小结 题目 from...import 随机模块 代码小结 randint函数 骰子大战 choice函数 总结 目标 拧一颗螺丝,只会用到螺丝刀;但是修一台汽车,需要一整套汽修的工具。函数就像螺丝刀,可以帮…

Go实战训练之Web Server 与路由树

Server & 路由树 Server Web 核心 对于一个 Web 框架,至少要提供三个抽象: Server:代表服务器的抽象Context:表示上下文的抽象路由树 Server 从特性上来说,至少要提供三部分功能: 生命周期控制&…

FIFO Generate IP核使用——Native读写接口信号详解

Native FIFO接口信号是用于FIFO IP核与外部电路进行通信的信号。当FIFO支持独立的写和读时钟时,这些信号可以包括标准端口和可选端口。 1 当FIFO具有独立时钟时的接口信号 当FIFO具有独立的时钟时,其接口信号会相应地有所变化。特别是关于复位信号rst…

Hibernate入门学习

目录 1、ORM思想概述 2、自定义ORM框架 3、第一个Hibernate程序开发步骤(重要) 1)下载完整包 2)创建项目,导入所需jar包 3)建立student表 4)创建和student表对应的Student实体类 5&…

postman中百度preview无法加载的解决方案

问题 在使用postman关联时,百度接口与天气接口已使用glb_city关联,但在百度接口发送请求时,发现preview无法加载 解决方案 1、进入百度 百度全球领先的中文搜索引擎、致力于让网民更便捷地获取信息,找到所求。百度超过千亿的中…

基于Springboot的民航网上订票系统(有报告)。Javaee项目,springboot项目。

演示视频: 基于Springboot的民航网上订票系统(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构…

vue3 + ts 快速入门(全)

文章目录 学习链接1. Vue3简介1.1. 性能的提升1.2.源码的升级1.3. 拥抱TypeScript1.4. 新的特性 2. 创建Vue3工程2.1. 基于 vue-cli 创建2.2. 基于 vite 创建(推荐)vite介绍创建步骤项目结构安装插件项目结构总结 2.3. 一个简单的效果Person.vueApp.vue …

11个2024年热门的AI编码助手

大家好,人工智能(AI)领域的大型语言模型(LLMs)已经逐渐发展成熟,并且深入到了我们日常的工作当中。在众多AI应用中,编码助手尤为突出,是开发人员编写更高效、准确无误代码的必备辅助…

docker原理

Docker原理 在前面我们学习了Docker,接下来我们探究一下Docker的底层技术原理 Linux 命名空间(namespace)、控制组(cgroups)和 联合文件系统(UnionFS) 三大技术支撑了目前 Docker 的实现&…

STM32入门学习之DMA

1.直接存储访问DMA(Direct Memory Access):DMA传输不需要CPU的参与,直接在内存和I/O设备间开辟了一条新的数据传输通道,不仅提高数据传输的速率,还因为不需要CPU的干预,从而提高了CPU的利用率。(注:文中的资…

OpenCV如何在图像中寻找轮廓(60)

返回:OpenCV系列文章目录(持续更新中......) 上一篇:OpenCV如何模板匹配(59) 下一篇 :OpenCV检测凸包(61) 目标 在本教程中,您将学习如何: 使用 OpenCV 函数 cv::findContours使用 OpenCV 函数 cv::d rawContours …

基于SSM的校园短期闲置资源置换平台(有报告)。Javaee项目。ssm项目。

演示视频: 基于SSM的校园短期闲置资源置换平台(有报告)。Javaee项目。ssm项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构,通过…

英语语法动词和动词的虚拟语气

动词 谓语动词的三大功能:时、态、气 1、时 和 态 的组合构成了英语的 16 种时态。 2、气 表示的是动作的情感和假设。