07. 协程及其模块

一、线程池和进程池的shutdown

作用:主线程/线程等待所有任务执行完成后,再将池子关闭,关闭以后就不能再提交任务了

代码演示:

from concurrent.futures import ThreadPoolExecutor
import time 

pool = ThreadPoolExecutor(3)

def task(name):
    print(f'{name}开始了')
    time.sleep(2)
    print(f'{name}结束了')

if __name__ == '__main__':
    for i in range(20):
        pool.submit(task,f'线程{i}')
    
    # 放到for循环外面
    pool.shutdown(wait=True)  # 等待所有任务结束,并且把池子关闭
    # 关闭以后不能再提交任务
    # pool.submit(task, 'aaa')
    print('主')  # 子线程的任务全部执行结束了就立马执行

二、定时器

作用:设定多长时间之后执行一个任务

使用Timer类实现,Timer类是Thread类的子类

代码演示:

from threading import Timer

def task(name):
	print(f'{name}:太难了,我怎么可以这么帅!')
if __name__ == '__main__':
    t = Timer(2, task, args=('cc',))  # 本质是开一个线程,延迟2秒执行
    t.start()

三、协程介绍

进程、线程、协程
-协程是为了实现单线程下的并发,又称微线程
	协程是一种用户态的轻量级线程,也就是说协程是由用户程序自己控制调度的。
	本质上是在单线程下,由用户自己控制一个任务遇到IO阻塞了就切换另一个任务去运行,以此来提升效率。
-协程要做的事情:
	1、可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续运行
    2、可以检测IO操作,遇到IO操作的情况下才发生切换
-协程并不是真实存在的事物,而是程序员臆想出来的
 程序员控制,不让自己的程序遇到IO,看上去,就实现并发了
-优点:
	① 协程的切换开销更小,属于程序级别的切换,操作体统完全感知不到,因而更加轻量级
    ② 单线程内就可以实现并发地效果,最大限度地利用CPU
-缺点:
	① 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启多个协程
    ② 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

总结:

1、必须在只有一个单线程里实现并发
2、修改共享数据不需要加锁
3、用户程序里袭击保存多个控制流的上下文线
4、一个协程遇到IO操作自动切换到其他协程(需要用到gevent模块的select机制,使用yield、greenlet无法实现)

代码演示yield关键字实现的协程:

import time

def func1():
    for i in range(10000000):
        i += 1
        yield 
def func2():  # 生成器
	g = func1()
    for i in range(10000000):
        i += 1
        next(g)
if __name__ == '__main__':
    ctime = time.time()
    func2()
    print(time.time() - ctime)

四、greenlet模块

应用场景:

如果在单个线程内有20个任务,要想实现多个任务之间切换,使用yield生成器就会过于麻烦

而使用greenlet模块就可以非常简单的实现

ps: greentlet模块是第三方模块,需要pip安装
代码示例:

from greenlet import greenlet
import time

def eat(name):
    print(f'{name}吃了一口')
    p.switch('cc')
    print(f'{name}又吃了一口')
    p.switch()


def play(name):
    print(f'{name}玩了一会')
    e.switch()
    time.sleep(1)
    print(f'{name}又玩了一会')


if __name__ == '__main__':
    e = greenlet(eat)
    p = greenlet(play)
    e.switch('cc')

**总结:**greenlet模块是一个初级模块,遇到IO操作不会切换,要想处理IO操作时的切换,需要使用基于它写的gevent模块

五、gevent模块

gevent模块是基于greenlet模块写的一个第三方模块,实现了遇见IO自动切换

可以轻松通过gevent实现并发同步或者一部编程,在gevent中用到的是greenlet

# 用法:
g1 = gevent.spawn(func,1,2,3,x=4,y=5) # 创建一个协程对象g1,spawn括号内的第一个参数是函数名,后面可以是位置实参或者关键字实参,都是传给func函数的
g2 = gevnet.spawn(func2)
g1.join()  # 等待g1结束
g2.join()  # 等待g2结束
# 或者上述两步合成一步: gevent.joinall([g1, g2])
g1.value  # 拿到func1的返回值

代码演示:

from gevent import monkey;monkey.patch_all()  # 使用猴子补丁,这一句必须写
import gevent
import time

def eat(name):
    print(f'{name}吃了一口')
    time.sleep(1)  # IO操作,使用猴子补丁将原来的gevent.sleep()替换了
    print(f'{name}又吃了一口')

def play(name):
    print(f'{name}玩了一会')
    time.sleep(2)
    print(f'{name}又玩了一会')
 
if __name__ = '__main__':
    ctime = time.time()
    e = gevent.spawn(eat, 'cc')
    p = gevent.spawn(play, 'cc')
    e.join()  # 等待e执行完成
    p.join()
    print('主')
    print(time.time() - ctime)

六、猴子补丁

class Monkey():
    def play(self):
        print('猴子在玩')
class Dog():
    def play(self):
        print('狗子在玩')
m = Monkey()
m.play = Dog().play
m.play()  # 使用猴子补丁完成替换

使用ujson模块替换json模块:

import json
import ujson

def monkey_patch_json():
    json.__name__ = 'ujson'
    json.dumps = ujson.dumps
    json.loads = ujson.loads

monkey_patch_ujson()

八、asyncio模块

asyncio模块是官方支持协程的库
使用方法:
-event_loop 时间循环:程序开启一个无限循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数
-coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,有事件循环调用
-task 任务:一个协程对象就是一个原生可以挂起的函数,任务就是对协程进一步封装,其中包含任务的各种状态
-future:代表将来执行或没有执行的任务的结果。它和task没有本质的区别
-async/await关键字:python3.5用于定义协程的关键字,async用于定义一个协程,await用于挂起阻塞的异步调用接口

python3.5之前的版本

import time
import asyncio

@asyncio.coroutine
def task():
    print('开始')
    yield from asyncio.sleep(1)  # 模拟IO
    print('结束')

loop = asyncio.get_event_loop()  # 获取一个时间循环对象
# 协程函数加括号并不会真正的执行,它需要提交给loop,让loop循环着去执行
# 协程函数列表

ctime = time.time()
t = [task(), task()]
lop.run_until_comlete(asyncio.wait(t))
loop.close()
print(time.time() - ctime)

新版本使用示例:

import time
import asyncio
from threading import current_thread

async def task1():  # 表示定义的是一个协程函数,等同于python3.5之前的装饰器
    print('开始')
    print(current_thread().name)
    await asyncio.sleep(3)  # await等同于之前的yield from
    print('结束')
async def task2():
    print('开始')
    print(current_thread().name)
    await asyncio.sleep(2)  # await等同于之前的yield from
    print('结束')

loop = asyncio.get_event_loop()

ctime = time.time()
t = [task1(),task2()]
loop.run_until_complete(asyncio.wait(t))
loop.close()
print(time.time() - ctime)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值