Python爬虫之线程、进程、协程详解

本文是我在学习过程中记录学习的点点滴滴,目的是为了学完之后巩固一下顺便也和大家分享一下,日后忘记了也可以方便快速的复习。


前言

今天学习的主要是关于在Python中线程、进程、协程的知识的理解和应用


一、多线程

概述:
同一进程内的线程共享内存空间(变量、资源),线程是资源分配的最小单位,也可以理解为线程是最小执行单位,就是真正做事情的人。
多线程就是一个进程里面有多个线程来执行任务。

在这里插入图片描述

1.1、多进程实例–创建两个子进程

# 线程类
import threading


# from threading import Thread

# 要执行的任务func()
def func(name):
    for i in range(100):
        print(name,i)

# 主线程
if __name__ == '__main__':
    # 创建一个子线程t1,并把任务func指派进去
    t1 = threading.Thread(target=func,args=("老大",))     #传参必须为元组
    # 多线程状态为可以开始工作状态,具体的执行时间由CPU决定
    t1.start()
    # 创建一个子线程t2,并把任务func指派进去
    t2 = threading.Thread(target=func, args=("老二",))    #传参必须为元组
    # 多线程状态为可以开始工作状态,具体的执行时间由CPU决定
    t2.start()
    # 主线程和多线程t会一起执行,这时func i和main i会交叉执行输出
    for i in range(100):
        print("main",i)

在这里插入图片描述

1.2、进阶写法

# 线程类
import threading

# 此为构造方法
class MyThread(threading.Thread):
    # 当线程被执行的时候,被执行的就是run()
    def run(self):
        for i in range(100):
            print("子线程", i)

# 主线程
if __name__ == '__main__':
    # 创建子线程
    t = MyThread()
    # 给子线程设为开始执行状态
    t.start()
    for i in range(100):
        print("主线程", i)

在这里插入图片描述

二、多进程

概述:
进程是资源单位,线程是执行单位,每一个进程至少要有一个线程,每个程序运行都默认有一个主线程,开辟多进程比较消耗资源,一般不建议开多进程

2.1、代码实例

# 引入多进程类
from multiprocessing import Process

# 要执行的任务func()
def func():
    for i in range(1000):
        print("子进程",i)

# 主进程
if __name__ == '__main__':
    # 创建一个子进程,并把任务func指派进去
    p = Process(target=func)
    # 多进程状态为可以开始工作状态,具体的执行时间由CPU决定
    p.start()
    # 主进程和多进程p会一起执行,这时func i 和main i 会交叉执行输出
    for i in range(1000):
        print("主进程",i)

三、线程池、进程池

线程池:一次性开辟一些线程,我们用户直接给线程池提交任务,线程任务的调度交给线程池来完成
进程池也是差不多的,进程池和线程池的不同就是在使用的上面不同,分别导入进程和线程池,然后创建的时候将ThreadPoolExecutor换成ProcessPoolExecutor即可

3.1、线程池

# 导入线程池
from concurrent.futures import ThreadPoolExecutor

# 要执行的任务
def fn(name):
    for i in range(1000):
        print(name,i)


if __name__ == '__main__':
    # 创建50个线程的线程池
        with ThreadPoolExecutor(50) as t:
            # 假设有100个任务
            for i in range(100):
                # 将100个任务交给线程池,线程池会安排50个线程来执行
                t.submit(fn,name=f"线程{i}")
        #等待线程池中的任务全部执行完毕,才继续执行{守护作用}
        print("123")

3.2、进程池

# 导入进程池
from concurrent.futures import ProcessPoolExecutor

# 要执行的任务
def fn(name):
    for i in range(1000):
        print(name,i)


if __name__ == '__main__':
    # 创建50个进程的进程池
        with ProcessPoolExecutor(50) as t:
            # 假设有100个任务
            for i in range(100):
                # 将100个任务交给进程池,进程池会安排50个进程来执行
                t.submit(fn,name=f"进程{i}")
        #等待进程池中的任务全部执行完毕,才继续执行{守护作用}
        print("123")

四、协程

一般情况下,程序在处于IO操作的时候,线程都是处于堵塞状态(例如input(),request.get()都会等待用户输入,等待服务器返回数据)
协程:当程序遇见了IO操作的时候,可以选择性的切换到其他任务上
在微观上是一个任务一个任务的进行切换,切换条件一般就是IO操作
在宏观上,我们能看到的其实是多个任务一起在执行
上方所讲的一切,都是在单线程的条件下

import time
import asyncio


async def func1():
    print("111")
    # time.sleep(1)               #当程序出现了同步操作,异步就中断了,那么这个程序相当于同步,一共花费6秒多
    await asyncio.sleep(1)        #异步操作的代码,三个协程异步一起运行,花费时间为最长的3秒钟
    print("111111")


async def func2():
    print("222")
    # time.sleep(2)               #当程序出现了同步操作,异步就中断了,那么这个程序相当于同步,一共花费6秒多
    await asyncio.sleep(2)        #异步操作的代码,三个协程异步一起运行,花费时间为最长的3秒钟
    print("222222")


async def func3():
    print("333")
    # time.sleep(3)               #当程序出现了同步操作,异步就中断了,那么这个程序相当于同步,一共花费6秒多
    await asyncio.sleep(3)        #异步操作的代码,三个协程异步一起运行,花费时间为最长的3秒钟
    print("333333")

# 为了代码优雅推荐如下写法,一般用一个方法来获取各个要执行的协程对象
# 一般await挂起操作放在协程对象前面
async def main():
    # 此时tasks里面返回的是三个协程对象
    #在Python3.8及以后,要使用asyncio.create_task(func1()),不能直接func1()
    tasks = [
        asyncio.create_task(func1()),
        asyncio.create_task(func2()),
        asyncio.create_task(func3())
    ]

    await asyncio.wait(tasks)



if __name__ == '__main__':
    t1 = time.time()
    #
    asyncio.run(main())
    t2 = time.time()
    print(t2-t1)

4.1、爬虫实战应用

#爬虫实战应用
import time
import asyncio

async def download(url):
    print("准备开始下载")
    await   asyncio.sleep(2)   #模拟网络请求
    print("下载完成")

async def main():
    urls = [
        "http://www.baidu.com",
        "http://www.google.com",
        "http://www.263.com",
    ]
    tasks = []
    for url in urls:
        d = download(url)
        tasks.append(d)
	#等待这组tasks协程执行完毕然后再返回结果
    await asyncio.wait(tasks)

if __name__ == '__main__':
	#开始执行协程
    asyncio.run(main())

总结:在协程中 asyncio.run(main()) 才是开始执行的命令操作,执行协程结果返回的是一个协程对象,尽量将这些对象放在一个tasks集合里面,然后使用 asyncio.wait(tasks) 等待这组tasks协程执行完毕

💕 原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

👍 点赞,你的认可是我创作的动力! \textcolor{orange}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

收藏,你的青睐是我努力的方向! \textcolor{red}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

🥕 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小福仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值