python-定时器_协程_greenlet模块_gevent模块_单线程的套接字并发_io模型

线程协程

1. 线程池和进程池的shutdown

# 主线程等待所有任务执行完成

import time
from concurrent.futures import ThreadPoolExecutor

pool = ThreadPoolExecutor(3)

def task(name):
    print('%s 开始'%name)
    time.sleep(1)
    print('%s 结束'%name)
    
if __name__ == '__main__':
    for i in range(20):
        pool.submit(task,'屌丝%s'%i)
        
    # 放到for外面
    pool.shutdown(wait=True) # 等待所有任务完成,并且把池关闭
    # pool.submit(task,'sddd') error
    print('主') # 立马执行,20个线程都执行完了,再执行

2. 定时器

# 多长时间之后执行一个任务
from threading import Timer

def task(name):
    print('我是大帅比--%s'%name)
    
if __name__ == '__main__':
    # t = Timer(2,task,args=('lqz',)) # 本质是开了个线程,延迟两秒执行
    t = Timer(2,task,kwargs={
   
   'name':'lqz'}) 
    t.start()

3. 协程介绍

# 进程  线程  协程
# 协程是为了实现单线程下的并发 属于线程下 又称微线程 协程是线程的具体实现
# 协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的
# 协程要解决的问题: 保存状态 + 切换
# yield: 生成器 只要函数中有yield关键字 这个函数就是生成器 通过yield可以实现保存状态 + 切换
'''
注意:
	1. python的线程属于内核级别的,即由操作系统控制调度
	   (如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
	2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,
	   以此来提升效率(非io操作的切换与效率无关)
'''

import time
# 串行执行
def func1():
    for i in range(1000000):
        i += 1
     
def func2():
    for i in range(1000000):
        i += 1
      
if __name__ == '__main__':
    ctime = time.time()
    func1()
    func2()
    print(time.time() - ctime) # 7s多
    
# 通过yield 实现保存状态加切换 (自定义的切换,并不是遇到io才切换,所以它并不能节约时间)
# 单纯的切换 不但不会提高效率 反而会降低效率
def func1():
    for i in range(1000000):
        i += 1
        yield
     
def func2():
    g = func1() # 先执行一下func1
    for i in range(1000000):
        i += 1
        next(g) # 回到func1执行
      
if __name__ == '__main__':
    ctime = time.time()
    func2()
    print(time.time() - ctime) # 14.764776706695557
    
# 协程并不是真正存在的某个东西,而是程序员臆想出来的
# 程序员控制,不让自己的程序遇到io,看上去就实现并发了

'''
协程优点:
	协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
	单线程内就可以实现并发的效果,最大限度的利用了cpu
协程缺点:
	协程的本质是在单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程
	每个线程内开启协程
	协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
协程特点:
	必须在只有一个单线程里实现并发
	修改共享数据不需要加锁
	用户程序里自己保持多个控制流的上下文栈 (需要保持状态)
	一个协程遇到io操作自动切换到其他协程
	(如何实现检测io,yield.greenlet都无法实现,就用到了gevent模块)
'''
1. 用协程改写生产者消费者模型
# 生产者
def prodecer():
	for i in range(100):
		yield i

# 消费者
def consumer(gen):
	for i in range(10):
		print(next(gen))

# 初始化生成器函数 -> 生成器
gen = prodecer()
consumer(gen)
consumer(gen)
consumer(gen)
2. 协程的具体实现 greenlet模块
# 案例一
from greenlet import greenlet
import time
"""
switch 遇到阻塞时,只能手动调用切换的函数,来规避阻塞
"""

def eat():
	print("eat 1")
	g2.switch()
	time.sleep(3)
	print("eat 2")
	
def play():
	print("play 1")
	time.sleep(3)
	print("play 2")
	g1.switch()

g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()

# 案例二
from greenlet import greenlet
import time
# 遇到io不会切,初级模式,gevent模块基于它写的,处理io切换
def eat():
    print('我吃了一口')
    time.sleep(1)
    p.switch()
    print('我又吃了一口')
    p.switch()
    
def play():
    print('我玩了一会')
    e.switch()
    print('我又玩了一会')
    
if __name__ == '__main__':
    e = greenlet(eat)
    p = greenlet(play)
    e.switch()

4. gevent模块 (第三方库)

# 猴子补丁 (一切皆对象)
# 拥有在模块运行时替换的功能, 例如: 一个函数对象赋值给另外一个函数对象(把函数原本的执行的功能给替换了)

class Monkey():
    def play(self):
        print('猴子在玩')
 
class Dog():
    def play(self):
        print('狗子在玩')
     
m = Monkey()
m.play()

m.play = Dog().play # 这个过程就叫猴子补丁,在程序运行过程中,动态修改
m.play()

# 猴子补丁 使用时需要加写
from gevent import monkey;monkey.patch_all()
import gevent
import time

def eat(name):
    print('%s 吃了一口'%name)
    time.sleep(1) # io操作,被猴子补丁替换之后,gevent.sleep()
    print('%s 又吃了一口'%name)
    
def play(name):
    print('%s 玩了一会' % name)
    time.sleep(2)
    print('%s 又玩了一会' % name)
    
if __name__ == '__main__':
    ctime = time.time()
    e = gevent.spawn(eat,'lqz')
    p = gevent.spawn(eat,'lqz')
    e.join()
    p.join()
    print('主')
    print(time.time()-ctime)
# gevent 可以实现切换,不能识别time.sleep阻塞

import gevent
def eat():
	print("eat 1")
	gevent.sleep(3)
	# time.sleep(3)
	print("eat 2")
	
def play():
	print("play 1")
	gevent.sleep(3)
	# time.sleep(3)
	print("play 2")

# 利用gevent.spawn创建协程对象g1
g1 = gevent.spawn(eat)
# 利用gevent.spawn创建协程对象g2
g2 = gevent.spawn(play)
	
# 如果不加join阻塞,默认主线程执行时,不等待直接结束.
# 阻塞,必须等待g1协程任务执行完毕之后,放行
g1.join()
# 阻塞,必须等待g2协程任务执行完毕之后,放行
g2.join()

# (4) 彻底解决gevent模块不识别阻塞的问题
from gevent import monkey
monkey.patch_all
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I believe I can fly~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值