Twenty-nine 协程
1、内容回顾
进程:
数据隔离
开启销毁切换开销大
multiprocessing
Process 开进程
Lock 保证数据安全
Queue IPC机制
Manager 数据共享类
Pool 池
线程:
数据共享
开启销毁切换开销小
线程是进程内部的一个执行单位
threading
Thread 开线程
Lock 在同一线程中只能acquire 一次
Rlock 在同一线程中可以联系acquire多次
queue
队列 先进先出
栈 后进先出
优先级队列 优先级高的先出
concurrent.futrues
进程池 ProcessPoolExecutor
CPU+1
线程池 ThreadPoolExecutor
CPU*5
2、协程
a、协程的本质 就是一条线程分成多份,每一份去执行一段代码
多段代码在一条线程内来回切换
b、如果一段代码遇到阻塞的过程中切换到另一段可以执行的代码上
就相当于完成利用协程完成了更加充分利用线程的目的
c、协程利用切换来规避IO操作带来的好处
a)一条线程能够执行多个任务
b)减少一条线程的阻塞,帮助线程在操作系统调度的时候多抢占CPU
c)协程由于是操作系统不可见,所以协程的切换不是由操作系统控制的
是由程序员控制的,用户级
d)协程之间永远数据安全-因为协程的本质是一条线程
3、用yield完成的协程函数
# 协程 # 在两个任务之间来回切换 import time def pro(): print(1) n = yield 'a' time.sleep(1) print(n) yield 'b' def com(): g = pro() a = next(g) print(a) b = g.send(2) print(b) com()
3、第三方模块安装方式:cmd或者pycharm
pytharm中安装:
不需要考虑pip、命令
你的计算机可能由多个python版本
一定保证安装在当前环境下之间使用
以后我们的开发环境、测试环境和生产环节
开发环境:你写代码用的环境
测试环境:测试人员用的环境
生产环境:你写代码实际对用户提供服务的时候所用的环境
cmd中安装
确认有pip.exe文件
pip.install 模块名 帮助我们完成模块以及其依赖模块的安装
4、greenlet底层模块(做状态的切换) gevent上层模块(做规避IO的切换)
from gevent import monkey monkey.patch_all() # 这句话之后导入模块的IO都会打成一个包,如果后面执行的IO模块跟包相关,就是IO操作 import time import gevent def eat(): print('eat1') # time.sleep(1) # 也有IO操作,但是没有进行切换,因为不认识,所以要monkey print('eat2') def sleep(): print('sleep1') time.sleep(1) print('sleep2') g1 = gevent.spawn(eat) # 告诉电脑是协程任务,但只有遇到阻塞事件才会执行 g2 = gevent.spawn(sleep) gevent.joinall([g1,g2]) # IO阻塞等待终止,精准预估到IO多久,time因为monkey相当于IO操作
5、爬虫的应用
def func(name,url): ret = request.urlopen(url) with open(name+'.html','wb') as f: f.write(ret.read()) url_lst = [ ('python','https://www.python.org/'), ('blog','http://www.cnblogs.com/Eva-J/articles/8324673.html'), ('pypi','https://pypi.org/project/pip/'), ('blog2','https://www.cnblogs.com/z-x-y/p/9237706.html'), ('douban','https://www.douban.com/') ] start = time.time() for url_item in url_lst: func(*url_item) end = time.time() print(end - start) from gevent import monkey monkey.patch_all() import gevent import time from urllib import request def func(name,url): ret = request.urlopen(url) with open(name+'2.html','wb') as f: f.write(ret.read()) start = time.time() g_l = [] for url_item in url_lst: g = gevent.spawn(func,*url_item) g_l.append(g) gevent.joinall(g_l) end = time.time() print(end - start)
6、并发一个协程server
from gevent import monkey monkey.patch_all() import socket import gevent def talk(conn): while True: msg = conn.recv(1024).decode('utf-8') conn.send(msg.upper().encode('utf-8')) sk = socket.socket() sk.bind(('127.0.0.1',9000)) sk.listen() while True: conn,addr = sk.accept() gevent.spawn(talk,conn) # 500个协程 一条线程 顶 500个线程的服务 # 进程的个数 CPU+1 # 线程个数 CPU*5 # 协程个数 500 # 5*20=100 * 500 =50000
7、事件:两个事情,需要一个事情确认一下另一个事情才能开始做
#如访问数据库的模拟连接代码 from threading import Event # 事件 # wait() 阻塞 到事件内部标识为True就停止阻塞 # 控制标识 # set # clear # is_set # 连接数据库 import time import random from threading import Thread,Event def connect_sql(e): count = 0 while count < 3: #连接3次 e.wait(0.5) #每次连接时间为0.5秒 if e.is_set(): print('连接数据库成功') break else: print('数据库未连接成功') count += 1 def test(e): time.sleep(random.randint(0,3)) #随机连接事件 e.set() e = Event() Thread(target=test,args=(e,)).start() Thread(target=connect_sql,args=(e,)).start()


浙公网安备 33010602011771号