不能无限的开进程,不能无限的开线程
最常用的就是开进程池,开线程池。其中回调函数非常重要
回调函数其实可以作为一种编程思想,谁好了谁就去调
只要你用并发,就会有锁的问题,但是你不能一直去自己加锁吧
那么我们就用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. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

 

  总结协程特点:

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 附加:一个协程遇到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 协程的更多相关文章

  1. Python(八)进程、线程、协程篇

    本章内容: 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 线程 Threading用于提供线程相关的操作.线程是应用 ...

  2. Lua的协程和协程库详解

    我们首先介绍一下什么是协程.然后详细介绍一下coroutine库,然后介绍一下协程的简单用法,最后介绍一下协程的复杂用法. 一.协程是什么? (1)线程 首先复习一下多线程.我们都知道线程——Thre ...

  3. 协程--gevent模块(单线程高并发)

    先恶补一下知识点,上节回顾 上下文切换:当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行.这种 ...

  4. Python 【第五章】:线程、进程和协程

    Python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. #!/usr/bin/env python # -*- coding:utf-8 -*- import t ...

  5. 进击的Python【第十章】:Python的socket高级应用(多进程,协程与异步)

    Python的socket高级应用(多进程,协程与异步)

  6. unity 协程

    StartCoroutine在unity3d的帮助中叫做协程,意思就是启动一个辅助的线程. 在C#中直接有Thread这个线程,但是在unity中有些元素是不能操作的.这个时候可以使用协程来完成. 使 ...

  7. golang 裸写一个pool池控制协程的大小

    这几天深入的研究了一下golang 的协程,读了一个好文 http://mp.weixin.qq.com/s?__biz=MjM5OTcxMzE0MQ==&mid=2653369770& ...

  8. 从Erlang进程看协程思想

    从Erlang进程看协程思想 多核慢慢火了以后,协程类编程也开始越来越火了.比较有代表性的有Go的goroutine.Erlang的Erlang进程.Scala的actor.windows下的fibr ...

  9. Unity学习疑问记录之协程

    http://blog.csdn.net/huang9012/article/details/38492937 总结:1.协程相当于多线程但不是,(尽管它们看上去是这样的),它们运行在同一线程中,跟普 ...

  10. python中协程

    在引出协成概念之前先说说python的进程和线程. 进程: 进程是正在执行程序实例.执行程序的过程中,内核会讲程序代码载入虚拟内存,为程序变量分配空间,建立 bookkeeping 数据结构,来记录与 ...

随机推荐

  1. 设计模式のMediatorPattern(中介者模式)----行为模式

    一.产生背景 从生活中的例子可以看出,不论是QQ游戏还是QQ群,它们都是充当一个中间平台,QQ用户可以登录这个中间平台与其他QQ用户进行交流,如果没有这些中间平台,我们如果想与朋友进行聊天的话,可能就 ...

  2. 用PHP打造一个高性能好用的网站

    用PHP打造一个高性能好用的网站 1. 说到高可用的话要提一下redis,用过的都知道redis是一个具备数据库特征的nosql,正好弥补了PHP的瓶颈,个人认为PHP的 瓶颈在于数据库,像Apach ...

  3. linux中启动 java -jar 运行程序

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 直接用java -jar xxx.jar,当退出或关闭shell时,程序就会停止掉.以 ...

  4. node访问oracledb的环境搭建

    关于安装oracleDB环境官网说明地址: https://oracle.github.io/node-oracledb/INSTALL.html 环境搭建所需软件和文档的压缩包 链接: https: ...

  5. utc时间转成local时间

    public static Date utcToLocal(String utcTime){ SimpleDateFormat sdf = new SimpleDateFormat("yyy ...

  6. Linux 分卷压缩

    例如,要将大文件夹 PYNQ 分卷压缩成 1G 的单元大小,如下命令(类似的可以指定 tar 的参数为 czf 而生产 .tar.gz 格式的压缩包:可以指定分卷大小例如 500M 等),压缩完成后, ...

  7. Pull is not possible because you have unmerged files

    Pull is not possible because you have unmerged files.   在git pull的过程中,如果有冲突,那么除了冲突的文件之外,其它的文件都会做为sta ...

  8. 【CTF杂项】常见文件文件头文件尾格式总结及各类文件头

    文件头文件尾总结 JPEG (jpg), 文件头:FFD8FF 文件尾:FF D9PNG (png), 文件头:89504E47 文件尾:AE 42 60 82GIF (gif), 文件头:47494 ...

  9. 【Codeforces 339】Xenia and Bit Operations

    Codeforces 339 D 题意:给定\(2^n​\)个数字,现在把它们进行如下操作: 相邻的两个数取\(or\) 相邻的两个数取\(xor\) 以此类推,直到剩下一个数. 问每次修改一个数字, ...

  10. mybatis抽取出的工具-(一)通用标记解析器(即拿即用)

    目录 1. 简介 1.1 mybatis-config.xml 中使用 1.2 xxxMapper.xml 中使用 2. 原理 2.1 GenericTokenParser 成员变量 2.2 Gene ...