Python并发
1、多进程(和多线程的方式类似)
2、多线程(2种编写方式)
2.1 submit方式
futures={pool.submit(get_html_from_url,url): url for url in urls}
for future in concurrent.futures.as_completed(futures):
data=future.result()
2.2 map方式
with ThreadPoolExecutor(max_workers=10)as pool:
for data in pool.map(spider.get_html_from_url_by_fpclient,urls):
3、异步(协程)
Python3版本引入了async/await特性,其特点是:当执行过程中遇到 IO 请求的时候,可以将CPU资源出让,运行其他的任务;待IO完成之后,继续执行之前的任务。协程切换与线程切换比较类似,但协程切换更轻,不需要操作系统参与(没有栈切换操作,也没有用户态与内核态切换)。
3.1 调用方式
3.1.1 在协程函数里去调用协程
import asyncio
async def func_1():
# 第一步:定义一个协程函数
print("start")
await asyncio.sleep(1)
print("end")
async def func_2():
# 第二步:在函数内部调用协程函数,自身也必须定义为协程
# func_1()调用产生了一个协程对象,通过await来执行这个协程。如果不加await,
# 直接以func_1()方式调用,则func_1中代码并不会执行。
await func_1()
loop = asyncio.get_event_loop()
# 第二步:创建事件大循环
"""Run the event loop until a Future is done."""
loop_result = loop.run_until_complete(func_1()) # 阻塞调用,直到协程运行结束才返回。
3.1.2 在非协程函数里去调用协程
一般情况下,无法在一个非协程函数中阻塞地调用另一个协程。
但你可以通过**asyncio.ensure_future()**来异步执行这个协程:
import asyncio
async def func_1():
# 第一步:定义一个协程函数
print("start")
await asyncio.sleep(1)
print("end")
def func_2():
asyncio.ensure_future(fun_1())
# 这里fun_1()将会在某个时间执行,具体执行顺序未知
# 这里是阻塞执行fun_1(),但这种调用,只能在event loop进入循环之前调用(loop.run_forever()),
# 否则会抛异常
asyncio.get_event_loop().run_until_complete(fun_1)
print("fun_1() is executed!")
3.2 协程睡眠
协程函数在执行中会占用本线程的全部CPU时间,除非遇到IO切换出去。
因此,如果你在函数中使用 time.sleep(),在多线程中,一个线程进入sleep状态,操作系统会切换到其它线程执行,整个程序仍然是可响应的(除了该线程,它必须等待睡眠状态结束);
而对协程来说,同一loop中的其它协程都不会得到执行,因为这个sleep会占用本线程的全部执行时间,直到协程执行完毕。
如果一个协程确实需要睡眠(比如某种定时任务)但是又不能影响同一线程中的其他协程,必须使用asyncio.sleep()
3.3 验证某一个函数是否为协程函数
print(asyncio.iscoroutinefunction(do_some_work)) # True
3.4 同时运行多个协程
两种方法
# 方法一:
loop.run_until_complete(asyncio.gather(do_some_work(3), do_some_work(5)))
# 方法二:先存成列表,在同时运行
coros = [do_some_work(1), do_some_work(3)]
loop.run_until_complete(asyncio.gather(*coros))
3.5 协程关闭
关闭循环:loop 只要不关闭,就还可以再运行。但是如果关闭了,就不能再运行了。
建议调用 loop.close,以彻底清理 loop 对象防止误用。
3.6 协程其他
异步IO:就是发起一个IO操作(如:网络请求,文件读写等),这些操作一般是比较耗时的,不用等待它结束,可以继续做其他事情,结束时会发来通知。
协程:又称为微线程,在一个线程中执行,执行函数时可以随时中断,由程序(用户)自身控制,执行效率极高,与多线程比较,没有切换线程的开销和多线程锁机制。
# 重要的概念
1.事件循环
管理所有的事件,在整个程序运行过程中不断循环执行并追踪事件发生的顺序将它们放在队列中,空闲时调用相应的事件处理者来处理这些事件。
2.Future
Future对象表示尚未完成的计算,还未完成的结果
3.Task
是Future的子类,作用是在运行某个任务的同时可以并发的运行多个任务。
asyncio.Task用于实现协作式多任务的库,且Task对象不能用户手动实例化,通过下面2个函数创建:
asyncio.async()
loop.create_task() 或 asyncio.ensure_future()
run_until_complete():
阻塞调用,直到协程运行结束才返回。参数是future,传入协程对象时内部会自动变为future
asyncio.sleep():
模拟IO操作,这样的休眠不会阻塞事件循环,前面加上await后会把控制权交给主事件循环,在休眠(IO操作)结束后恢复这个协程。
若在协程中需要有延时操作,应该使用 await asyncio.sleep(),而不是使用time.sleep(),因为使用time.sleep()后会释放GIL,阻塞整个主线程,从而阻塞整个事件循环。
3、2
# 创建Task
loop.create_task():
接收一个协程,返回一个asyncio.Task的实例,也是asyncio.Future的实例,毕竟Task是Future的子类。返回值可直接传入run_until_complete()
返回的Task对象可以看到协程的运行情况
"""
import asyncio
async def coroutine_example():
print("start1")
await asyncio.sleep(1)
print('end1')
return 1
async def coroutine_example2():
print("start2")
await asyncio.sleep(1)
print('end2')
return 2
loop = asyncio.get_event_loop()
coro = coroutine_example()
coro2 = coroutine_example2()
task = loop.create_task(coro)
task2 = loop.create_task(coro2)
print('运行情况1:', task)
print('运行情况2:', task2)
loop.run_until_complete(task)
print('再看下运行情况1:', task)
print('再看下运行情况2:', task2)
loop.close()
3、3
# -*- encoding: utf-8 -*-
"""ankus
@Modify Time @Author @Version @Description
------------ ------- -------- -----------
2021/12/3 14:21 xlgui2 1.0 获取协程返回值
有2种方案可以获取返回值。
第1种方案:通过task.result()
可通过调用 task.result() 方法来获取协程的返回值,
但是只有运行完毕后才能获取,若没有运行完毕,result()方法不会阻塞去等待结果,
而是抛出 asyncio.InvalidStateError 错误
第2种方案:通过add_done_callback()回调
"""
import asyncio
##########################################################################
# ## 第1种方案:通过task.result()
"""
async def coroutine_example():
await asyncio.sleep(1)
return 1
loop = asyncio.get_event_loop()
coro = coroutine_example()
task = loop.create_task(coro)
print('运行情况:', task)
try:
print('返回值:', task.result())
except asyncio.InvalidStateError:
print('task状态未完成,捕获了 InvalidStateError 异常')
loop.run_until_complete(task)
print('再看下运行情况:', task)
print('返回值:', task.result())
loop.close()
"""
##########################################################################
# ##第2种方案:通过add_done_callback()回调
def my_callback(future):
print('返回值:', future.result())
async def coroutine_example():
print("start")
await asyncio.sleep(1)
print("end")
return 1
loop = asyncio.get_event_loop()
coro = coroutine_example()
task = loop.create_task(coro)
task.add_done_callback(my_callback)
loop.run_until_complete(task)
loop.close()
# -*- encoding: utf-8 -*-
"""ankus
@Modify Time @Author @Version @Description
------------ ------- -------- -----------
2021/12/3 14:46 xlgui2 1.0 控制任务
通过asyncio.wait()可以控制多个任务
asyncio.wait()是一个协程,不会阻塞,立即返回,返回的是协程对象。
传入的参数是future或协程构成的可迭代对象。最后将返回值传给run_until_complete()加入事件循环
"""
import asyncio
async def coroutine_example(name):
print('正在执行name:', name)
await asyncio.sleep(3)
print('执行完毕name:', name)
loop = asyncio.get_event_loop()
tasks = [coroutine_example('task_' + str(i)) for i in range(9)] # 由协程构成的可迭代对象
wait_coro = asyncio.wait(tasks)
loop.run_until_complete(wait_coro)
loop.close()
# -*- encoding: utf-8 -*-
"""
方案1:需要通过loop.create_task()创建task对象,以便后面来获取返回值
方案2:通过回调add_done_callback()来获取返回值
"""
# 下面代码asyncio.wait()中,参数传入的是由future(task)对象构成的可迭代对象
##############################################
# 方案1:需要通过loop.create_task()创建task对象,以便后面来获取返回值
"""
import asyncio
async def coroutine_example(name):
print('正在执行name:', name)
await asyncio.sleep(1)
print('执行完毕name:', name)
return '返回值:' + name
loop = asyncio.get_event_loop()
tasks = [loop.create_task(coroutine_example('Zarten_' + str(i))) for i in range(3)]
wait_coro = asyncio.wait(tasks)
loop.run_until_complete(wait_coro)
for task in tasks:
print(task.result())
loop.close()
"""
########################
# 方案2:通过回调add_done_callback()来获取返回值
import asyncio
def my_callback(future):
print('返回值:', future.result())
async def coroutine_example(name):
print('正在执行name:', name)
await asyncio.sleep(1)
print('执行完毕name:', name)
return '返回值:' + name
loop = asyncio.get_event_loop()
tasks = []
for i in range(3):
task = loop.create_task(coroutine_example('Zarten_' + str(i)))
task.add_done_callback(my_callback)
tasks.append(task)
wait_coro = asyncio.wait(tasks)
loop.run_until_complete(wait_coro)
loop.close()