进程、线程、协程

进程示例

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
import time
from multiprocessing import Process


def get_request(url):
print('正在请求网址的数据:', url)
time.sleep(2)
print('请求结束:', url)


if __name__ == "__main__":
start = time.time()
urls = ['www.1.com', 'www.2.com', 'www.3.com']
p_list = [] # 存储创建好的子进程
for url in urls:
# 创建子进程
p = Process(target=get_request, args=(url,))
p_list.append(p)
# p.join() #一定不要这么写
# 启动子进程
p.start()

for pp in p_list: # pp就是列表中的每一个子进程
pp.join() # 是的每一个子进程都执行了join操作
# 意味着:主进程需要等待所有执行了join操作的子进程结束后再结束

print('总耗时:', time.time() - start)


执行

1
2
3
4
5
6
7
正在请求网址的数据: www.1.com
正在请求网址的数据: www.2.com
正在请求网址的数据: www.3.com
请求结束: www.1.com
请求结束: www.2.com
请求结束: www.3.com
总耗时: 2.026388645172119

线程示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from multiprocessing.dummy import Pool #导入了线程池模块
import time
from threading import Thread
start = time.time()
def get_requests(url):
print('正在爬取数据')
time.sleep(2)
print('数据爬取结束')

urls = ['www.1.com','www.2.com','www.3.com','www.4.com','www.5.com']
ts = []
for url in urls:
t = Thread(target=get_requests,args=(url,))
t.start()
ts.append(t)
for t in ts:
t.join()

print('总耗时:',time.time()-start)

执行

1
2
3
4
5
6
7
8
9
10
11
正在爬取数据
正在爬取数据
正在爬取数据
正在爬取数据
正在爬取数据
数据爬取结束
数据爬取结束
数据爬取结束
数据爬取结束
数据爬取结束
总耗时: 2.0125858783721924

线程池

线程预先被创建并放入线程池中,同时处理完当前任务之后并不销毁而是被安排处理下一个任务,因此能够避免多次创建线程,从而节省线程创建和销毁的开销,能带来更好的性能和系统稳定性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from multiprocessing.dummy import Pool #导入了线程池模块
import time
urls = ['www.1.com','www.2.com','www.3.com','www.4.com','www.5.com']
def get_reqeust(url):
print('正在请求数据:',url)
time.sleep(2)
print('请求结束:',url)
start = time.time()
#创建一个线程池,开启了5个线程
pool = Pool(5)
#可以利用线程池中三个线程不断的去处理5个任务
pool.map(get_reqeust,urls)
#get_reqeust函数调用的次数取决urls列表元素的个数
#get_requests每次执行都会接收urls列表中的一个元素作为参数

print('总耗时:',time.time()-start)
pool.close() #释放线程池

执行

1
2
3
4
5
6
7
8
9
10
11
12
正在请求数据:正在请求数据: www.2.com
www.1.com
请求结束: 请求结束: www.1.com
www.2.com
正在请求数据: www.3.com
正在请求数据: www.4.com
请求结束: www.4.com
正在请求数据: www.5.com
请求结束: www.3.com
请求结束: www.5.com
总耗时: 6.045103311538696

协程示例

回调函数用法

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
# 特殊的函数:
# 在函数定义前添加一个async关键字,该函数就变成了一个特殊的函数。
# 特殊函数的特殊之处:
# 1. 特殊函数被调用后,函数内部的程序语句不会被立即执行.
# 2. 特殊函数被调用后,会返回一个协程对象
# 协程:
# 特殊函数调用后,返回一个协程对象(协程对象是由特殊函数创建)
# 协程 == 特殊的函数 == 函数 == 一组指定形式的操作
# 协程 == 一组指定形式的操作
# 任务对象:
# 任务对象本质上是一个高级的协程。高级之处是什么?后面讲!
# 任务对象 == 高级协程 == 协程 == 一组指定形式的操作
# 任务对象 == 一组指定形式的操作
#
# 事件循环loop:
# 当做是一种容器。该容器是用来装载多个任务对象。
# loop就可以将其内部装载的任务对象进行异步的执行。

import asyncio
import time

async def get_request(url):
print('正在请求的网址是:',url)
time.sleep(2)
print('请求网址结束!')
return 123

#如何获取特殊函数内部的返回值(任务对象回调函数来实现的)
c = get_request('www.1.com')
task = asyncio.ensure_future(c)
#给任务对象绑定一个回调函数(回头调用的函数),该函数一定是在任务对象被执行完毕后再调用的函数
def task_callback(t): #必须有且仅有一个参数
#函数的参数t就是回调函数的调用者task任务对象本身
ret = t.result() #任务对象调用result()就可以返回特殊函数的内部return后的结果值
print('我是回调函数,我被执行了,t.result()返回的结果是:',ret)
#给task任务对象绑定了一个叫做task_callback的回调函数
task.add_done_callback(task_callback)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)

执行

1
2
3
正在请求的网址是: www.1.com
请求网址结束!
我是回调函数,我被执行了,t.result()返回的结果是: 123

多任务示例

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
45
46
47
48
49
50
51
# 特殊的函数:
# 在函数定义前添加一个async关键字,该函数就变成了一个特殊的函数。
# 特殊函数的特殊之处:
# 1. 特殊函数被调用后,函数内部的程序语句不会被立即执行.
# 2. 特殊函数被调用后,会返回一个协程对象
# 协程:
# 特殊函数调用后,返回一个协程对象(协程对象是由特殊函数创建)
# 协程 == 特殊的函数 == 函数 == 一组指定形式的操作
# 协程 == 一组指定形式的操作
# 任务对象:
# 任务对象本质上是一个高级的协程。高级之处是什么?后面讲!
# 任务对象 == 高级协程 == 协程 == 一组指定形式的操作
# 任务对象 == 一组指定形式的操作
#
# 事件循环loop:
# 当做是一种容器。该容器是用来装载多个任务对象。
# loop就可以将其内部装载的任务对象进行异步的执行。

import asyncio
import time

start = time.time()

async def get_request(url):
#特殊函数内部不可以出现不支持异步模块的代码,否则会中断整个异步效果
print('正在请求的网址是:',url)
#没有加await关键字之前:每一个任务中的阻塞操作并没有被执行
#await关键字:必须要加在每一个任务的阻塞操作前,作用就是强调执行任务中的阻塞操作
#await是用来手动控制任务的挂起操作。
await asyncio.sleep(2)
print('请求网址结束!')
return 123

urls = [
'www.1.com','www.2.com','www.3.com'
]

tasks = [] #定义一个任务列表
for url in urls: #循环3次
#创建了3个协程
c = get_request(url)
#创建3个任务对象
task = asyncio.ensure_future(c)
tasks.append(task) #将创建好的3个任务对象依次存放到了tasks这个任务列表中

loop = asyncio.get_event_loop()
#将任务列表tasks添加到loop容器中
#wait()函数:用于接收一个任务列表,wait函数就可以给任务列表中每一个任务对象赋予一个可被挂起的权限
#一个任务被挂起,就表示当前任务对象交出了cpu的使用权
loop.run_until_complete(asyncio.wait(tasks))
print('总耗时:',time.time()-start)

执行

1
2
3
4
5
6
7
正在请求的网址是: www.1.com
正在请求的网址是: www.2.com
正在请求的网址是: www.3.com
请求网址结束!
请求网址结束!
请求网址结束!
总耗时: 2.006136417388916

aiohttp

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
45
46
47
48
49
50
import asyncio
import time
import aiohttp
# import requests

from lxml import etree
start = time.time()

urls = [
'http://127.0.0.1:5000/bobo',
'http://127.0.0.1:5000/jay',
'http://127.0.0.1:5000/tom'
]

#发起网络请求,爬取网页完整数据
async def get_request(url):
#不可以出现不支持异步模块的代码
#requests是不支持异步,更换一个支持异步的网络请求的模块(aiohttp)
# response = requests.get(url=url)
# page_text = response.text
# return page_text

#aiohttp进行网络请求的代码操作
#1.创建一个请求对象:sess
async with aiohttp.ClientSession() as sess:
#2.使用请求对象发起请求
#aiohttp发起请求的代码操作和requests几乎一致,唯一不一致的地方是,使用代理的参数proxy="http://ip:port"
async with await sess.get(url=url) as response:
#text()获取字符串形式的响应数据
#read()获取二进制形式的响应数据
#json()和以前的作用一致
page_text = await response.text()
return page_text
tasks = []
def parse(t): #数据解析
page_text = t.result()
tree = etree.HTML(page_text)
text = tree.xpath('//a[@id="feng"]/text()')[0]
print(text)
for url in urls:
c = get_request(url)
task = asyncio.ensure_future(c)
#给任务对象绑定回调函数用于数据解析
task.add_done_callback(parse)
tasks.append(task)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

print('总耗时:',time.time()-start)

进程、线程、协程
http://example.com/2023/12/07/进程、线程、协程/
Author
John Doe
Posted on
December 7, 2023
Licensed under