day 7-8 协程
- 不能无限的开进程,不能无限的开线程
最常用的就是开进程池,开线程池。其中回调函数非常重要
回调函数其实可以作为一种编程思想,谁好了谁就去调
- 只要你用并发,就会有锁的问题,但是你不能一直去自己加锁吧
那么我们就用QUEUE,这样还解决了自动加锁的问题
由Queue延伸出的一个点也非常重要的概念。以后写程序也会用到
这个思想。就是生产者与消费者问题
一、Python标准模块--concurrent.futures(并发未来)
- concurent.future模块需要了解的
1.concurent.future模块是用来创建并行的任务,提供了更高级别的接口,
为了异步执行调用
2.concurent.future这个模块用起来非常方便,它的接口也封装的非常简单
3.concurent.future模块既可以实现进程池,也可以实现线程池
4.模块导入进程池和线程池
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
5.p = ProcessPoolExecutor(max_works)对于进程池如果不写max_works:默认的是cpu的数目,默认是4个
p = ThreadPoolExecutor(max_works)对于线程池如果不写max_works:默认的是cpu的数目*5
6.如果是进程池,得到的结果如果是一个对象。我们得用一个.get()方法得到结果
但是现在用了concurent.future模块,我们可以用obj.result方法
p.submit(task,i) #相当于apply_async异步方法
p.shutdown() #默认有个参数wite=True (相当于close和join)
二、线程池
- 进程池:就是在一个进程内控制一定个数的线程
基于concurent.future模块的进程池和线程池 (他们的同步执行和异步执行是一样的)
线程池内生成一定数量的线程,当遇到I/O时切换.并不是说有多少个任务,就就多少个线程.
- from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
- import os
- import time
- import random
- # I/O密集型的用线程池,用进程池的话开销大,效率低
- #----------------同步执行-----------------
- def task(n):
- print("%s is runing"%os.getpid())
- time.sleep(random.randint(1,3))
- return n**2
- if __name__ == '__main__':
- temp_li=[]
- start = time.time()
- p=ProcessPoolExecutor(max_workers=4)
- for i in range(10):
- obj=p.submit(task,i).result() #等待结果,相当于apple同步方法.永远都会只是4个进程,就算有100个任务,也是4个进程轮流切换
- temp_li.append(obj)
- p.shutdown() #相当于close和join
- print(temp_li)
- print("耗时:%s"%(time.time()-start))
- #---------------异步执行------------------
- def task(n):
- print("%s is running"%os.getpid())
- time.sleep(random.randint(1,3))
- return n**2
- if __name__ == '__main__':
- temp_li=[]
- start=time.time()
- p=ProcessPoolExecutor(max_workers=4) #如果不填写max_workers,默认是cpu核数
- for i in range(10):
- obj = p.submit(task,i) #不等待结果,提交完就走
- print(obj) #打印进程状态的话,会看到有些是running(运行态),有些是pending(就绪).
- temp_li.append(obj)
- p.shutdown()
- print([obj.result() for obj in temp_li])
- print("耗时:%s"%(time.time() - start))
基于concurrent.future进程池
- from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
- import os
- import time
- import random
- def task(n):
- print("%s is running"%os.getpid())
- time.sleep(random.randint(1,3))
- return n**2
- if __name__ == '__main__':
- temp_li=[]
- start=time.time()
- t=ThreadPoolExecutor() #如果不填写max_workers,默认是cpu核数*5
- for i in range(10):
- obj =t.submit(task,i) #不等待结果,提交完就走
- print(obj) #打印进程状态的话,会看到有些是running(运行态),有些是pending(就绪).
- temp_li.append(obj)
- t.shutdown()
- print([obj.result() for obj in temp_li])
- print("耗时:%s"%(time.time() - start)) #3.003171682357788
基于concurrent.future线程池
- from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
- import requests
- import time
- import os
- def get_page(url):
- print("%s is getting %s"%(os.getpid(),url))
- response = requests.get(url)
- if response.status_code==200:#200表示下载成功的状态码
- return {"url":url,"text":response.text}
- def pares_page(res):
- res = res.result()
- print("%s is getting %s"%(os.getpid(),res["url"]))
- with open("db1.txt","a",encoding="utf-8") as f:
- pares_res = "url:%s size:%s \n" %(res["url"],len(res["url"]))
- f.write(pares_res)
- if __name__ == '__main__':
- p = ProcessPoolExecutor()
- # p = ThreadPoolExecutor()
- li =[
- "http://www.baidu.com",
- "http://www.google.com",
- "http://www.youporn.com"
- ]
- for url in li:
- res = p.submit(get_page,url).add_done_callback(pares_page)#回调函数.
- p.shutdown()
- print("main",os.getpid())
线程池应用
map函数的应用
- # map函数举例
- obj= map(lambda x:x**2 ,range(10))
- print(list(obj))
- #运行结果[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
- #! -*- coding:utf-8 -*-
- from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
- import os
- import time
- import random
- def task(n):
- print("%s is running"%os.getpid())
- time.sleep(random.randint(1,3))
- return n**2
- if __name__ == '__main__':
- temp_li=[]
- start=time.time()
- t=ThreadPoolExecutor() #如果不填写max_workers,默认是cpu核数*5
- obj = t.map(task,range(10))
- t.shutdown()
- print(list(obj))
- print("耗时:%s"%(time.time() - start))
map函数
三,协程
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
优点:
- 1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
- 2. 单线程内就可以实现并发的效果,最大限度地利用cpu
缺点:
1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
总结协程特点:
- 必须在只有一个单线程里实现并发
- 修改共享数据不需加锁
- 用户程序里自己保存多个控制流的上下文栈
- 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
四,greenlet模块
如果我们现在有一个单线程,里面有10个任务,那么如果我们使用yield生成器来实现的话,太麻烦了(初始化生成器,调用send).这就用到了greenlet模块了.
Greenlet模块和yield没有什么区别,就只是单纯的切,跟效率无关。只不过比yield更好一点,切的时候方便一点。但是仍然没有解决效率.Greenlet可以让你在多个任务之间来回的切.
安装模块:
pip3 install greenlet
- #! -*- coding:utf-8 -*-
- from greenlet import greenlet
- def eat(name):
- print("%s is eating 1"%name)
- g2.switch("jack")
- print("%s is eating 2"%name)
- g2.switch()
- def running(name):
- print("%s is running"%name)
- g1.switch()
- print("%s is not running"%name)
- if __name__ == '__main__':
- g1 = greenlet(eat)
- g2= greenlet(running)
- g1.switch("alex")
greenlet例子
单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度.
- import time
- def f1():
- res=1
- for i in range(100000000):
- res+=i
- def f2():
- res=1
- for i in range(100000000):
- res*=i
- start=time.time()
- f1()
- f2()
- stop=time.time()#10.354592561721802
- from greenlet import greenlet
- import time
- def f1():
- res=1
- for i in range(100000000):
- res+=i
- g2.switch()
- def f2():
- res=1
- for i in range(100000000):
- res*=i
- g1.switch()
- start=time.time()
- g1=greenlet(f1)
- g2=greenlet(f2)
- g1.switch()
- stop=time.time()
- print('run time is %s' %(stop-start)) # 51.55694890022278
单纯的切换,反而降低效率
greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。单线程里的这10个任务的代码通常会既有计算操作又有阻塞操作,我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提高效率,这就用到了Gevent模块。
五,gevnet模块
安装:
- pip3 install gevent
Gevent是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet,它是以C扩展模块形式接入Python的轻量级协程。Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。
用法:
- import gevent
- def test(name):
- print("%s is eating"%name)
- return 1111
- def test2(name):
- print("%s is going"%name)
- return 3333
- # g1 = gevent.spawn(函数名,位置参数(*args),关键字参数(**kwargs))
- g1 = gevent.spawn(test,"jack")
- g2 = gevent.spawn(test2,"alex")
- gevent.joinall([g1,g2]) #等待g1,g2结束,也可以写成单个g1.join(),g2.join()
- # g1.join()
- print(g1.value) #拿到返回值
- print(g2.value)
gevnet的一些方法:
- # from gevent import monkey;monkey.patch_all()
- import gevent
- import time
- def eat(name):
- print("%s is eat"%name)
- # time.sleep(1.5) #模拟IO阻塞
- """
- 如果使用time.sleep()表示时间等待的话,需要在代码顶部加入一行 from gevent import monkey;monkey.patch_all()
- 如果使用gevent.sleep()则无需加入代码
- """
- gevent.sleep(1.5)
- print("%s is eat 1"%name)
- return "eat"
- def play(name):
- print('%s play 1'%name)
- # time.sleep(3)
- gevent.sleep(3)
- print('%s play 2'%name)
- return 'paly' # 当有返回值的时候,gevent模块也提供了返回结果的操作
- start_time = time.time()
- g1 = gevent.spawn(eat,"jack")
- g2 = gevent.spawn(play,"Lucy")
- gevent.joinall([g1,g2])
- print("main",time.time()-start_time)
- print(g1.value)
- print(g2.value)
注意time.sleep()和gevent.sleep()
注意:
gevent.sleep(1.5)模拟的是gevent可以识别的io阻塞,
而time.sleep(1.5)或其他的阻塞,gevent是不能直接识别的需要用下面一行代码,打补丁,就可以识别了
from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前
或者我们干脆记忆成:要用gevent,需要将from gevent import monkey;monkey.patch_all()放到文件的开头
- from gevent import monkey;monkey.patch_all()
- import gevent
- import time
- def task(pid):
- time.sleep(0.5)
- print("Task %s is done" % pid)
- def synchronous(): #同步
- for i in range(10):
- task(i)
- def asynchronoues(): #异步
- g_l = [gevent.spawn(task,i) for i in range(10)]
- print(g_l)
- gevent.joinall(g_l)
- if __name__ == '__main__':
- print("sync")
- synchronous()
- print("async")
- asynchronoues()
- #上面程序的重要部分是将task函数封装到Greenlet内部线程的gevent.spawn。 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,后者阻塞当前流程,并执行所有给定的greenlet。执行流程只会在 所有greenlet执行完后才会继续向下走。
同步异步
day 7-8 协程的更多相关文章
- Python(八)进程、线程、协程篇
本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用 ...
- Lua的协程和协程库详解
我们首先介绍一下什么是协程.然后详细介绍一下coroutine库,然后介绍一下协程的简单用法,最后介绍一下协程的复杂用法. 一.协程是什么? (1)线程 首先复习一下多线程.我们都知道线程——Thre ...
- 协程--gevent模块(单线程高并发)
先恶补一下知识点,上节回顾 上下文切换:当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行.这种 ...
- Python 【第五章】:线程、进程和协程
Python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. #!/usr/bin/env python # -*- coding:utf-8 -*- import t ...
- 进击的Python【第十章】:Python的socket高级应用(多进程,协程与异步)
Python的socket高级应用(多进程,协程与异步)
- unity 协程
StartCoroutine在unity3d的帮助中叫做协程,意思就是启动一个辅助的线程. 在C#中直接有Thread这个线程,但是在unity中有些元素是不能操作的.这个时候可以使用协程来完成. 使 ...
- golang 裸写一个pool池控制协程的大小
这几天深入的研究了一下golang 的协程,读了一个好文 http://mp.weixin.qq.com/s?__biz=MjM5OTcxMzE0MQ==&mid=2653369770& ...
- 从Erlang进程看协程思想
从Erlang进程看协程思想 多核慢慢火了以后,协程类编程也开始越来越火了.比较有代表性的有Go的goroutine.Erlang的Erlang进程.Scala的actor.windows下的fibr ...
- Unity学习疑问记录之协程
http://blog.csdn.net/huang9012/article/details/38492937 总结:1.协程相当于多线程但不是,(尽管它们看上去是这样的),它们运行在同一线程中,跟普 ...
- python中协程
在引出协成概念之前先说说python的进程和线程. 进程: 进程是正在执行程序实例.执行程序的过程中,内核会讲程序代码载入虚拟内存,为程序变量分配空间,建立 bookkeeping 数据结构,来记录与 ...
随机推荐
- JavaScript的对象详解
JavaScript对象的概述 什么是对象,代表现实中的某个事物, 是该事物在编程中的抽象,多个数据的集合体(封装体),用于保存多个数据的容器 为什么要用对象,便于对多个数据进行统一管理 对象属于一种 ...
- E - Intervals 贪心
Chiaki has n intervals and the i-th of them is [li, ri]. She wants to delete some intervals so that ...
- c# 链接mongDB集群实战开发3
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/zuoming120/article/details/25702295 c# 链接mongDB集群 一 ...
- [MySQL性能优化系列] 聚合索引
1. 普通青年的索引使用方式 假设我们有一个用户表 tb_user,内容如下: name age sex jack 22 男 rose 21 女 tom 20 男 ... ... ... 执行SQL语 ...
- Nginx系列一:正向代理和反向代理、Nginx工作原理、Nginx常用命令和升级、搭建Nginx负载均衡
转自https://www.cnblogs.com/leeSmall/p/9351343.html 仅供个人学习 一.什么是正向代理.什么是反向代理 1. 正向代理,意思是一个位于客户端和原始服务器( ...
- Consul在.Net Core中初体验
Consul在.Net Core中初体验 简介 在阅读本文前我想您应该对微服务架构有一个基本的或者模糊的了解 Consul是一个服务管理软件,它其实有很多组件,包括服务发现配置共享键值对存储等 本文主 ...
- CDB与PDB之间的切换方法
Oracle 12c 开始支持 PLUGGABLE DATABASE,并且提供了一个方法在CDB和PDB之间切换. 1. 使用 show pdbs 可以确认当前有哪些PDB? SQL> show ...
- Red Hat 7.2 RPM安装Mysql 5.7.12
安装Red Hat 7.2 开发包Java包全部安装 下载Mysql 5.7.12 wget http://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5 ...
- 【C#复习总结】细说 Lambda表达式
1 前言 本系列会将[委托] [匿名方法][Lambda表达式] [泛型委托] [表达式树] [事件]等基础知识总结一下.(本人小白一枚,有错误的地方希望大佬指正) 系类1:细说委托 系类2:细说匿名 ...
- elasticsearch简单操作(一)
1.增加记录 例如1:向指定的 /Index/Type 发送 PUT 请求,就可以在 Index 里面新增一条记录.比如,向/accounts/person发送请求,就可以新增一条人员记录. curl ...