python协程

进程是资源分配的最小单位,线程是CPU调度的最小单位python

进程
    开销大 数据隔离(资源分配的最小单位) 可以利用多核
线程
    开销小 资源共享(cpu调度的最小单位)  可以利用多核(Cpython解释器下不行)

Cpython解释器flask

高计算型 开多进程
高IO型   开多线程
因此大部分状况下,基本上只要有网络操做,开启多线程必定能够提升这一整个程序的cpu使用率

线程centos

GIL 全局解释器锁 致使了线程不能利用多核
线程之间数据安全的问题
    Lock互斥锁
    Rlock递归锁
    死锁现象: 同一个线程中,出现了两把锁,同时锁两个资源去进行某些操做
线程队列
    先进先出的队列
    后进先出的栈
    优先级队列
池
    concurrent.futrues
    处理文件操做(处理日志文件) 处理网络并发(请求爬虫的页面)
    ThreadPoolExcutor  ProcessPoolExcutor
    submit
    shutdown
    map
    result
    add_done_callback

协程介绍安全

线程中可以放多个任务在一个线程中,其中的每个任务均可以成为一个协程,每个协程均可以在一条线程中任意的切换 线程: cpython解释器 只能用一核 只有有IO操做的多个任务才适合起多线程 协程的本质就是在单线程下,由用户本身控制一个任务遇到io阻塞了就切换另一个任务去执行,以此来提高效率 优势: 1.协程的切换开销更小,属于程序级别的切换,操做系统彻底感知不到,于是更加轻量级 2.单线程内就能够实现并发的效果,最大限度地利用cpu 缺点: 1.协程的本质是单线程下,没法利用多核,能够是一个程序开启多个进程,每一个进程内开启多个线程,每一个线程内开启协程 2.协程指的是单个线程,于是一旦协程出现阻塞,将会阻塞整个线程

串行执行网络

import time def consumer(res): '''任务1:接收数据,处理数据'''
    pass

def producer(): '''任务2:生产数据''' res=[] for i in range(10000000): res.append(i) return res start=time.time() #串行执行
res=producer() #写成consumer(producer())会下降执行效率
consumer(res) stop=time.time() print(stop-start)

基于yield并发执行多线程

import time def consumer(): '''任务1:接收数据,处理数据'''
    while True: x=yield

def producer(): '''任务2:生产数据''' g=consumer() next(g) for i in range(10000000): g.send(i) start=time.time() #基于yield保存状态,实现两个任务直接来回切换,即并发的效果
producer() stop=time.time() print(stop-start)

greenlet模块介绍:并发

模块安装: pip install greenlet import time from greenlet import greenlet def eat(): print('start eating') # 保存当前函数的状态 切换到对应的函数中去执行
 g2.switch() time.sleep(1) print('eating finished') def sleep(): print('start sleeping') time.sleep(1) print('sleeping finished') g1.switch() # 建立了一个协程对象
g1 = greenlet(eat) # 建立了一个协程对象
g2 = greenlet(sleep) g1.switch()

yield   快
greenlet 慢app

gevent模块介绍:框架

pip3 install gevent 使用协程 是为了规避io,由于程序:遇到io就切换 gevent遇到IO就切换,基于greenlet进行切换 from threading import currentThread from gevent import monkey monkey.patch_all() import time # 内部使用了greenlet的切换机制 实现了遇到IO自动进行切换
import gevent def eat(): # DummyThread-1 伪线程
    print('start eating',currentThread()) time.sleep(1) print('eating finished') def sleep(): # DummyThread-1 伪线程
    print('start sleeping',currentThread()) time.sleep(1) print('sleeping finished') g_l = [] for  i in range(5): #建立一个协程对象g1,spawn括号内第一个参数是函数名,后面能够有多个参数,能够是位置实参或关键字实参,都是传给函数
    g1 = gevent.spawn(eat) g2 = gevent.spawn(sleep) g_l.append(g1) g_l.append(g2) #等待g1结束 # g1.join() # g2.join()
 gevent.joinall(g_l)

两个协程模块异步

gevent - 基于greenlet - 使用更方便 性能相对低 asyncio - 基于yield  - 性能更好 flask 支持协程 gevent的 twisted sanic tornado 异步框架 用的是asyncio协程模块
让gevent识别的io操做是来自其余模块的(要写在其余模块以前) from gevent import monkey monkey.patch_all() import gevent import time from urllib import request def get_page(url_t): ret = request.urlopen(url_t[0]) content = ret.read() with open(url_t[1]+'_new','wb') as f: f.write(content) url_lst = [ ('http://www.sogou.com','sogou'), ('http://www.baidu.com','baidu'), ('http://www.douban.com','douban'), ('http://www.cnblogs.com','cnblogs'), ('https://www.redhat.com/en','redhat'), ('https://www.centos.org/','centos'), ('https://www.liaoxuefeng.com/','lxf'), ('http://www.jd.com','jd'), ('http://www.taobao.com','tb') ] start = time.time() g_l = [] for url_t in url_lst: g = gevent.spawn(get_page,url_t) g_l.append(g) gevent.joinall(g_l) print(time.time() - start)

协程模拟socket server多客户端通讯

server端: from gevent import monkey monkey.patch_all() import socket import gevent def talk(conn): while True: msg = conn.recv(1024).decode() ret_msg = msg.upper().encode() conn.send(ret_msg) sk = socket.socket() sk.bind(('127.0.0.1',9000)) sk.listen() while True: conn,_ = sk.accept() g = gevent.spawn(talk,conn) client端: import socket import threading def client(): sk = socket.socket() sk.connect(('127.0.0.1',9000)) while True: sk.send(b'hello') msg = sk.recv(1024) print(msg) for i in range(500): threading.Thread(target=client).start()

总结:

asyncio的状态切换是由yield关键字完成的 greenlet 扩展模块 进行函数中代码的来回切换 在其余编译型语言中 因为多线程能够利用多核 因此协程这个概念被弱化了 对于Cpython 多线程也不能利用多核 因此协程这个概念就变得相当重要了 协程的本质是一条线程 1.不能利用多核 2.用户级的概念,操做系统不可见 3.协程不存在数据安全问题 切换 一条线程 操做系统感知到线程一直在运行 就减小了线程进入阻塞状态的次数,从而提升了效率 协程实际的定义: 在一条线程之间来回切换 1.yield和函数做比较 yield进行切换实际上会浪费时间,即使是程序级别的切换也会浪费时间 2.500个client并发的server的效率计算,协程的处理并发能力 很强 3.geturl爬虫的练习,协程和函数作对比,协程的速度快 gevent去处理问题实际上很简便 直接扔给spawn就好了 建议 进程数 cpu个数的1-2倍 对于其余的语言 来讲 进程是不常开的 线程数 20 协程数 500并发 进程 + 协程 开5个进程,在进程中开协程 既可以利用多核 也能够提升处理IO的效率 线程 处理 文件 ,input 进程 8个进程 协程 1000个 4*500 = 8000并发
相关文章
相关标签/搜索