Python高级(进程、线程和协程)

​ 进程:运行中的程序. 每次我们执行一个程序, 咱们的操作系统对自动的为这个程序准备一些必要的资源(例如,分配内存,创建一个能够执行的线程。)

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

进程与线程之间的关系:

​ 进程是资源单位(公司),线程是执行单位(员工),就好比是一家公司,一家公司的资源就是桌椅板凳,电脑饮水机这些资源,但是,我们如果说一家公司正在运转着,运行着,那里面必须要有能为这家公司工作的人。程序里面也一样,进程就是为了程序运行而需要的各种资源。但是程序想要运行,就必须由线程来被CPU调度执行。

​ 我们运行的每一个程序默认都会有一个线程,哪怕是只有helloworld级别的程序,想要执行,也会有一个线程产生。

线程

​ 顾名思义,多线程就是让程序产生多个线程一起去执行。 还拿公司举例子,一家公司里如果只有一个员工,工作效率肯定不会高到哪里去,怎么提高效率?多招点儿人就OK了。

直接使用Thread创建线程

单线程

def work():
    for i in range(1000):
        print("func", i)


if __name__ == '__main__':
    work()

多线程

import threading
# 类似于同一个公司开设不同的部门
# 或者同一个部门有多个小组

def work():
    for i in range(1000):
        print(f"{threading.current_thread().name} Print: {i}")  # 打印当前线程名称

if __name__ == '__main__':
    for i in range(10):
        t = threading.Thread(target=work, name=f"threadP{i}")  # 设置线程名称
        t.start()

线程池

python还提供了线程池功能,可以一次性的创建多个线程,并且,不需要我们程序员手动去维护。一切都交给线程池来自动管理。

from concurrent.futures.thread import ThreadPoolExecutor

def work(name):
    for i in range(10000):
        print(f'{name}:{i}')

with ThreadPoolExecutor(16) as t:  # 设置线程池,大小一般为CPU核心数的2倍,这里设置的最大线程数为16
    for i in range(4):
        t.submit(work, f'线程{i}')

进程

一个公司能创造的价值毕竟是有限的,怎么办?开分公司啊.,此所谓多进程,python实现多进程的方案和多线程几乎一样,非常的简单。

from multiprocessing import Process
# 一个公司能创造的价值毕竟是有限的. 怎么办?
# 开分公司啊. 此所谓多进程. python实现多进程的方案和多线程几乎一样. 非常的简单
def func():
    for i in range(1000):
        print("func", i)


if __name__ == '__main__':
    p = Process(target=func)
    p.start()

    for i in range(1000):
        print("main", i)

协程

​ 协程是我要重点去讲解的一个知识点,它能够更加高效的利用CPU。

​ 其实, 我们能够高效的利用多线程来完成爬虫其实已经很6了,但是,从某种角度讲,线程的执行效率真的就无敌了么?我们真的充分的利用CPU资源了么?非也~ 比如,我们来看下面这个例子。

我们单独的用一个线程来完成某一个操作,看看它的效率是否真的能把CPU完全利用起来。

import time

def func():
    print("我爱黎明")
    time.sleep(3)
    print("我真的爱黎明")
    
func()

​ 各位请看,在该程序中,我们的func()实际在执行的时候至少需要3秒的时间来完成操作,中间的三秒钟需要让我当前的线程处于阻塞状态。阻塞状态的线程 CPU是不会来执行你的,那么此时cpu很可能会切换到其他程序上去执行。此时,对于你来说,CPU其实并没有为你工作(在这三秒内),那么我们能不能通过某种手段,让CPU一直为我而工作,尽量的不要去管其他人。

​ 我们要知道CPU一般抛开执行周期不谈,如果一个线程遇到了IO操作,CPU就会自动的切换到其他线程进行执行。那么,如果我想办法让我的线程遇到了IO操作就挂起,留下的都是运算操作,那CPU是不是就会长时间的来照顾我。

​ 以此为目的,伟大的程序员就发明了一个新的执行过程。当线程中遇到了IO操作的时候,将线程中的任务进行切换,切换到其他任务。等原来的IO执行完了,再恢复回原来的任务中。

image-20210308154852699

就形成了这样一种模型,在程序遇到了IO操作(费时不费力的操作)时,自动切换到其他任务,该模型被称为协程。

协程基本使用

需要使用关键词asyncawait

  • async是定义一个异步函数的关键字,异步函数是协程的一种。它用于定义一个可以在执行过程中挂起的函数,该函数可以在遇到await关键字时挂起,并等待另一个协程执行完毕后再继续执行。
  • await则是用于挂起当前协程并等待另一个协程执行完毕的关键字。当遇到await关键字时,当前协程将会暂停执行,直到等待的协程执行完毕,并返回结果后才会继续执行。
  • async/await的使用可以大大简化异步编程的复杂性,使得在Python中实现高效的异步程序变得更加容易。通过使用async/await,我们可以将一些常见的异步操作抽象为协程,从而使得我们可以使用类似于同步代码的方式来实现异步操作。
import time
import asyncio

# await: 当该任务被挂起后, CPU会自动切换到其他任务中
async def func1():
    print("func1, start")
    await asyncio.sleep(3)
    print("func1, end")


async def func2():
    print("func2, start")
    await asyncio.sleep(4)
    print("func2, end")


async def func3():
    print("func3, start")
    await asyncio.sleep(2)
    print("func3, end")


async def run():
    start = time.time()
    tasks = [  # 协程任务列表
        asyncio.create_task(func1()),  # create_task创建协程任务
        asyncio.create_task(func2()),
        asyncio.create_task(func3()),
    ]
    await asyncio.wait(tasks)  # 等待所有任务执行结束
    print(time.time() - start)

if __name__ == '__main__':
    asyncio.run(run())

协程异步IO操作

协程可以结合相关异步的第三方库,达到异步网页请求和文件操作操作,最大化使用CPU资源。

  • aiohttp:异步HTTP请求库,类似于requests库。
  • aiofiles:异步文件操作库,一般用于文件读写。
import aiohttp
import asyncio
import aiofiles


async def download(url):
    try:
        name = url.split("/")[-1].split('@')[0]
        # 创建session对象 -> 相当于requsts对象
        async with aiohttp.ClientSession() as session:
            # 发送请求, 这里和requests.get()几乎没区别, 除了代理换成了proxy
            async with session.get(url, ssl=False) as resp:  # 取消SSL验证
                # # resp.text(encoding='') 这可以设置字符集
                # 读取数据. 如果想要读取源代码. 直接resp.text()即可. 比原来多了个()
                content = await resp.content.read()
                # 写入文件, 用默认的open也OK. 用aiofiles能进一步提升效率
                async with aiofiles.open('../download_files/' + name, mode="wb") as f:
                    await f.write(content)
                    return "OK"
    except Exception as e:
        print(e)
        return "NO"


async def main():
    url_list = [
        "https://cdn-usa.skypixel.com/uploads/usa_files/storynode/image/214c3e7a-767d-41b3-9dcc-c43df9b2e5fd.jpg@!1200",
        "https://cdn-usa.skypixel.com/uploads/usa_files/storynode/image/4b32f894-a8e0-4d92-9dc7-f9c5b932723f.JPG@!1200"
    ]
    tasks = []

    for url in url_list:
        # 创建任务
        task = asyncio.create_task(download(url))
        tasks.append(task)

    await asyncio.wait(tasks)


if __name__ == '__main__':
    # asyncio.run(main())  用这句话会报错RuntimeError: Event loop is closed
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值