0%

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

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

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

进程与线程之间的关系:

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

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

线程

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

直接使用Thread创建线程

单线程

1
2
3
4
5
6
7
def work():
for i in range(1000):
print("func", i)


if __name__ == '__main__':
work()

多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
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还提供了线程池功能,可以一次性的创建多个线程,并且,不需要我们程序员手动去维护。一切都交给线程池来自动管理。

1
2
3
4
5
6
7
8
9
10
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实现多进程的方案和多线程几乎一样,非常的简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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完全利用起来。

1
2
3
4
5
6
7
8
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,我们可以将一些常见的异步操作抽象为协程,从而使得我们可以使用类似于同步代码的方式来实现异步操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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:异步文件操作库,一般用于文件读写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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())

-------------本文结束感谢您的阅读-------------