Python全栈之协程详解

目录
  • 1. 线程队列
  • 2. 进程池_线程池
  • 3. 回调函数
  • 4. 协程
  • 总结:

1. 线程队列

# ### 线程队列
from queue import Queue
"""
put 存放 超出队列长度阻塞
get 获取 超出队列长度阻塞
put_nowait 存放,超出队列长度报错
get_nowait 获取,超出队列长度报错
"""
# (1) Queue
"""先进先出,后进先出"""
q = Queue()
q.put(100)
q.put(200)
print(q.get())
# print(q.get())
# print(q.get()) 阻塞
# print(q.get_nowait())
# print(q.get_nowait()) 报错
# Queue(3)  =>  指定队列长度, 元素个数只能是3个;
q2 = Queue(3)
q2.put(1000)
q2.put(2000)
# q2.put(3000)
# q2.put(4000) 阻塞
q2.put_nowait(6000)
# q2.put_nowait(4000) 报错
# (2) LifoQueue
"""先进后出,后进先出(栈的特点)"""
from queue import LifoQueue
lq = LifoQueue()
lq.put(110)
lq.put(120)
lq.put(119)
print(lq.get())
print(lq.get())
print(lq.get())
# (3) PriorityQueue
"""按照优先级顺序进行排序存放(默认从小到大)"""
"""在一个优先级队列中,要放同一类型的数据,不能混合使用"""
from queue import PriorityQueue
pq = PriorityQueue()
# 1.对数字进行排序
pq.put(100)
pq.put(19)
pq.put(-90)
pq.put(88)
print(pq.get())
print(pq.get())
print(pq.get())
print(pq.get())
# 2.对字母进行排序 (按照ascii编码)
pq.put("wangwen")
pq.put("sunjian")
pq.put('wangwei')
pq.put("王文")
pq.put("孙坚")
pq.put('王维')
print( pq.get() )
print( pq.get() )
print( pq.get() )
print( pq.get() )
print( pq.get() )
print( pq.get() )
# 3.对容器进行排序
pq.put( (22,"wangwen") )
pq.put( (67,"wangyuhan") )
pq.put( (3,"anxiaodong") )
pq.put( (3,"liuyubo") )
print(pq.get())
print(pq.get())
print(pq.get())
print(pq.get())
# 4.注意点
pq.put(100)
pq.put("nihao")
pq.put( (1,2,3) )

2. 进程池_线程池

知识点:

# 线程池
    # 实例化线程池      ThreadPoolExcutor    (推荐5*cpu_count)
    # 异步提交任务      submit / map
    # 阻塞直到任务完成   shutdown
    # 获取子线程的返回值 result
    # 使用回调函数      add_done_callback
# 线程池 是由子线程实现的
# 进程池 是由主进程实现的

程序实现:

# ### 进程池 和 线程池
from concurrent.futures import ProcessPoolExecutor , ThreadPoolExecutor
import os,time,random
# 获取的逻辑处理器
# print(os.cpu_count())
"""多条进程提前开辟,可触发多cpu的并行效果"""
'''
# (1) 进程池 ProcessPoolExecutor
def func(i):
	# print(i)
	time.sleep(random.uniform(0.1,0.8))
	print(" 任务执行中 ...  start ... 进程号{}".format(os.getpid()) , i )
	print(" 任务执行中 ...  end ... 进程号{}".format(os.getpid()))
	return i
if __name__ == "__main__":
	lst = []
	# (1) 创建进程池对象
	"""默认参数是 系统最大的逻辑核心数 4"""
	p = ProcessPoolExecutor()
	# (2) 异步提交任务
	"""submit(任务,参数1,参数2 ... )"""
	"""默认如果一个进程短时间内可以完成更多的任务,进程池就不会使用更多的进程来辅助完成 , 可以节省系统资源的损耗;"""
	for i in range(10):
		obj = p.submit( func , i )
		# print(obj)
		# print(obj.result()) 不要写在这,导致程序同步,内部有阻塞
		lst.append(obj)
	# (3) 获取当前任务的返回值
	for i in lst:
		print(i.result(),">===获取返回值===?")
	# (4) shutdown 等待所有进程池里的进程执行完毕之后,在放行
	p.shutdown()
	print("进程池结束 ... ")
'''
# (2) ThreadPoolExecutor
'''
# from threading import currentThread as ct
from threading import current_thread as ct
def func(i):
	print(" 任务执行中 ...  start ... 线程号{}".format( ct().ident ) , i )
	time.sleep(1)
	print(" 任务执行中 ...  end ... 线程号{}".format(os.getpid()))
	return ct().ident  # 线程号
if __name__ == "__main__":
	lst = []
	setvar = set()
	"""默认参数是 系统最大的逻辑核心数 4 * 5 = 20"""
	# (1) 创建线程池对象
	t = ThreadPoolExecutor() # 20
	# print(t)
	# (2) 异步提交任务
	"""默认如果一个线程短时间内可以完成更多的任务,线程池就不会使用更多的线程来辅助完成 , 可以节省系统资源的损耗;"""
	for i in range(100):
		obj = t.submit(func,i)
		lst.append(obj)
	# (3) 获取当前任务的返回值
	for i in lst:
		setvar.add(i.result())
	# (4) shutdown 等待所有线程池里的线程执行完毕之后,在放行
	t.shutdown()
	print("主线程执行结束 ... ")
	print(setvar , len(setvar))
'''
# (3) 线程池 map
from threading import currentThread as ct
from collections import Iterator,Iterable
def func(i):
	time.sleep(random.uniform(0.1,0.7))
	print("thread ... 线程号{}".format(ct().ident),i)
	return "*" * i
if __name__ == "__main__":
	t = ThreadPoolExecutor()
	it = t.map(func,range(100))
	# 返回的数据是迭代器
	print(isinstance(it,Iterator))
	# 协调子父线程,等待线程池中所有线程执行完毕之后,在放行;
	t.shutdown()
	# 获取迭代器里面的返回值
	for i in it:
		print(i)
"""
# 总结: 无论是进程池还是线程池,都是由固定的进程数或者线程数来执行所有任务
系统不会额外创建多余的进程或者线程来执行任务;
"""

3. 回调函数

知识点:

# 回调函数
    就是一个参数,将这个函数作为参数传到另一个函数里面.
    函数先执行,再执行当参数传递的这个函数,这个参数函数是回调函数

程序实现:

# ### 回调函数
"""
回调函数: 回头调用一下函数获取最后结果
微信支付宝付款成功后, 获取付款金额
微信支付宝退款成功后, 获取退款金额
一般用在获取最后的状态值时,使用回调
通过add_done_callback最后调用一下自定义的回调函数;
"""
from concurrent.futures import ProcessPoolExecutor , ThreadPoolExecutor
from threading import currentThread as ct
import os,time,random
"""进程任务"""
def func1(i):
	time.sleep(random.uniform(0.1,0.9))
	print(" 进程任务执行中 ...  start ... 进程号{}".format(os.getpid()) , i )
	print(" 进程任务执行中 ...  end ... 进程号{}".format(os.getpid()) )
	return i
def call_back1(obj):
	print(   "<==回调函数的进程号{}==>".format(os.getpid())   )
	print(obj.result())
"""线程任务"""
def func2(i):
	time.sleep(random.uniform(0.1,0.9))
	print(" 线程任务执行中 ...  start ... 线程号{}".format(ct().ident) , i )
	print(" 线程任务执行中 ...  end ... 线程号{}".format( ct().ident) )
	return i
def call_back2(obj):
	print(   "<==回调函数的线程号{}==>".format(  ct().ident) )
	print(obj.result())
if __name__ == "__main__":
	"""
	# (1)进程池  结果:(进程池的回调函数由主进程执行)
	p = ProcessPoolExecutor() # os.cpu_count()  => 4
	for i in range(1,11):
		obj = p.submit(func1 , i )
		# 使用add_done_callback在获取最后返回值的时候,可以异步并行
		obj.add_done_callback(call_back1)
		# 直接使用result获取返回值的时候,会变成同步程序,速度慢;
		# obj.result()
	p.shutdown()
	print(   "主进程执行结束...进程号:"    ,    os.getpid()  )
	"""
	print("<==============================================>")
	# (2)线程池  结果:(线程池的回调函数由子线程执行)
	t = ThreadPoolExecutor()
	for i in range(1,11):
		obj = t.submit(func2 , i )
		# 使用add_done_callback在获取最后返回值的时候,可以异步并发
		obj.add_done_callback(call_back2)
		# 直接使用result获取返回值的时候,会变成同步程序,速度慢;
		# obj.result()
	t.shutdown()
	print("主线程执行结束 .... 线程号{}".format(ct().ident))
"""
# 原型:
class Ceshi():
	def add_done_callback(self,func):
		print("系统执行操作1 ... ")
		print("系统执行操作2 ... ")
		# 回头调用一下
		func(self)
	def result(self):
		return 112233
def call_back(obj):
	print(obj.result())
obj = Ceshi()
obj.add_done_callback(call_back)
"""

4. 协程

知识点:

#协程也叫纤程: 协程是线程的一种实现方式.
    指的是一条线程能够在多任务之间来回切换的一种实现.
    对于CPU、操作系统来说,协程并不存在.
    任务之间的切换会花费时间.
    目前电脑配置一般线程开到200会阻塞卡顿.
#协程的实现
协程帮助你记住哪个任务执行到哪个位置上了,并且实现安全的切换
一个任务一旦阻塞卡顿,立刻切换到另一个任务继续执行,保证线程总是忙碌的,更加充分的利用CPU,抢占更多的时间片
# 一个线程可以由多个协程来实现,协程之间不会产生数据安全问题
#协程模块
    # greenlet  gevent的底层,协程,切换的模块
    # gevent    直接用的,gevent能提供更全面的功能

程序实现:

# ### 协程
"""
进程是资源分配的最小单位
线程是程序调度的最下单位
协程是线程实现的具体方式
总结:
在进程一定的情况下,开辟多个线程,
在线程一定的情况下,创建多个协程,
以便提高更大的并行并发
"""
# (1) 用协程改写生产者消费者模型
"""
def producer():
	for i in range(1000):
		yield i
def consumer(gen):
	for i in range(10):
		print(  next(gen)  )
gen = producer()
consumer(gen)
print("<==========>")
consumer(gen)
print("<==========>")
consumer(gen)
"""
# (2) greenlet 协程的早期版本
from greenlet import greenlet
import time
""" switch 可以切换任务,但是需要手动切换"""
"""
def eat():
	print("eat1")
	g2.switch()
	time.sleep(3)
	print("eat2")
def play():
	print("play1")
	time.sleep(3)
	print("play2")
	g1.switch()
g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()
"""
# (3) 升级到gevent版本
"""自动进行任务上的切换,但是不能识别阻塞"""
"""
import gevent
def eat():
	print("eat1")
	gevent.sleep(3)
	# time.sleep(3)
	print("eat2")
def play():
	print("play1")
	gevent.sleep(3)
	# time.sleep(3)
	print("play2")
# 利用gevent.spawn创建协程对象g1
g1 = gevent.spawn(eat)
# 利用gevent.spawn创建协程对象g2
g2 = gevent.spawn(play)
# 如果不加join, 主线程直接结束任务,不会默认等待协程任务.
# 阻塞,必须等待g1任务完成之后在放行
g1.join()
# 阻塞,必须等待g2任务完成之后在放行
g2.join()
print("主线程执行结束 ....  ")
"""
# (4) 协程的终极版本;
from gevent import monkey;monkey.patch_all()
"""引入猴子补丁,可以实现所有的阻塞全部识别"""
import time
import gevent
def eat():
	print("eat1")
	time.sleep(3)
	print("eat2")
def play():
	print("play1")
	time.sleep(3)
	print("play2")
# 利用gevent.spawn创建协程对象g1
g1 = gevent.spawn(eat)
# 利用gevent.spawn创建协程对象g2
g2 = gevent.spawn(play)
# 如果不加join, 主线程直接结束任务,不会默认等待协程任务.
# 阻塞,必须等待g1任务完成之后在放行
g1.join()
# 阻塞,必须等待g2任务完成之后在放行
g2.join()
print(" 主线程执行结束 ... ")
"""
# 分号,利用分号可以把多行代码放在一行进行编写;
a = 1
b = 2
a = 1;b = 2
"""

==理解:==一个线程上有好多任务,协程可以记住每个任务完成的状态,比如做饭的时候做到一半的时候停下来,去扫地,扫完地之后拐回来做饭,从做到一半的时候开始做。

小提示: 下载gevent包,会自带greenlet

早期版本的想到在time.sleep执行了两次,每次执行了一秒钟,切换回来有执行了一秒,这是模拟早期版本,模拟堵塞

总结:

p.shutdown() 这里的shutdown类似于join
生成器在实例化对象的时候,里面的代码是不走的,调用的时候才有,next
调用等
单线程实现的一种异步并发的一种结构
协程能记住任务的状态

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 深入理解python协程

    一.什么是协程 协程拥有自己的寄存器和栈.协程调度切换的时候,将寄存器上下文和栈都保存到其他地方,在切换回来的时候,恢复到先前保存的寄存器上下文和栈,因此:协程能保留上一次调用状态,每次过程重入时,就相当于进入上一次调用的状态. 协程的好处: 1.无需线程上下文切换的开销(还是单线程) 2.无需原子操作(一个线程改一个变量,改一个变量的过程就可以称为原子操作)的锁定和同步的开销 3.方便切换控制流,简化编程模型 4.高并发+高扩展+低成本:一个cpu支持上万的协程都没有问题,适合用于高并发处理

  • Python 协程与 JavaScript 协程的对比

    目录 1.前言 2.什么是协程? 3.混乱的历史 3.1 Python 协程的进化 4.JavaScript 协程的进化 5.Python 协程成熟体 5.1 协程(coroutine) 5.2 任务(Task 对象) 5.3 未来对象(Future) 5.4几种事件循环(event loop) 6.JavaScript 协程成熟体 6.1Promise 继续使用 6.2 async.await语法糖 6.3 js 异步执行的运行机制 6.4 event loop 将任务划分 7.总结与对比 1

  • 浅谈Python协程asyncio

    一.协程 官方描述; 协程是子例程的更一般形式. 子例程可以在某一点进入并在另一点退出. 协程则可以在许多不同的点上进入.退出和恢复. 它们可通过 async def 语句来实现. 参见 PEP 492. 协程不是计算机内部提供的,不像进程.线程,由电脑本身提供,它是由程序员人为创造的, 实现函数异步执行. 协程(Coroutine),也可以被称为微线程,是一种用户太内的上下文切换技术,其实就是通过一个线程实现代码块相互切换执行.看上去像子程序,但执行过程中,在子程序内部可中断,然后转而执行别的

  • Python协程asyncio 异步编程笔记分享

    目录 1.事件循环 2.协程和异步编程 2.1 基本使用 2.2 await 2.3 Task对象 1.事件循环 可以理解成为一个死循环,去检查任务列表中的任务,如果可执行就去执行,如果检查不到就是不可执行的,那就忽略掉去执行其他可执行的任务,如果IO结束了(比如说去百度下载图片,下载完了就会变成可执行任务)再去执行下载完成之后的逻辑 #这里的任务是有状态的,比如这个任务已经完成或者正在执行或者正在IO等待 任务列表 = [ 任务1, 任务2, 任务3,... ] while True: 可执行

  • python asyncio 协程库的使用

    asyncio 是 python 力推多年的携程库,与其 线程库 相得益彰,更轻量,并且协程可以访问同一进程中的变量,不需要进程间通信来传递数据,所以使用起来非常顺手. asyncio 官方文档写的非常简练和有效,半小时内可以学习和测试完,下面为我的一段 HelloWrold,感觉可以更快速的帮你认识 协程 . 定义协程 import asyncio import time async def say_after(delay, what): await asyncio.sleep(delay)

  • Python获取协程返回值的四种方式详解

    目录 介绍 源码 依次执行结果 介绍 获取协程返回值的四种方式: 1.通过ensure_future获取,本质是future对象中的result方 2.使用loop自带的create_task, 获取返回值 3.使用callback, 一旦await地方的内容运行完,就会运行callback 4.使用partial这个模块向callback函数中传入值 源码 import asyncio from functools import partial async def talk(name): pr

  • Python全栈之协程详解

    目录 1. 线程队列 2. 进程池_线程池 3. 回调函数 4. 协程 总结: 1. 线程队列 # ### 线程队列 from queue import Queue """ put 存放 超出队列长度阻塞 get 获取 超出队列长度阻塞 put_nowait 存放,超出队列长度报错 get_nowait 获取,超出队列长度报错 """ # (1) Queue """先进先出,后进先出"""

  • Python全栈之列表数据类型详解

    前言 列表(list)同字符串一样都是有序的,因为他们都可以通过切片和索引进行数据访问,且列表是可变的. 创建列表的几种方法 第一种 name_list = ['Python', 'PHP', 'JAVA'] 第二种 name_list = list(['Python', 'PHP', 'JAVA']) 创建一个空列表 >>> li = list() >>> type(li) <class 'list'> 把一个字符串转换成一个列表 >>>

  • Python全栈之模板渲染详解

    目录 1.标签 1.1for循环标签 1.2if标签 1.3with标签 1.4csrftoken标签 2.模板继承 3.组件 4.自定义过滤器 5.自定义标签 6.inclusion_tag自定义标签 7.小提示与小练习 总结 1. 标签 {% 标签 %} 1.1 for循环标签 <ul> <!-- 可迭代对象都可以用循环 --> <!-- 循环列表 --> {% for foo in hobby %} <li>{{ foo }}</li> {

  • C++ Boost Coroutine使用协程详解

    目录 一.说明语言扩展 二.库Boost.Coroutine 三.示例和代码 一.说明语言扩展 以下库扩展了编程语言 C++. Boost.Coroutine 使得在 C++ 中使用协程成为可能——其他编程语言通常通过关键字 yield 支持. Boost.Foreach 提供了一个基于范围的 for 循环,它是在 C++11 中添加到语言中的. Boost.Parameter 允许您以名称/值对的形式并以任何顺序传递参数——例如,这在 Python 中是允许的. Boost.Conversio

  • Python3.10 Generator生成器Coroutine原生协程详解

    目录 引言 协程底层实现 业务场景 结语 引言 普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态.而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手段,就是生成器,或者说的更具体一些:协程就是一种特殊的生成器,而生成器,就是协程的入门心法. 协程底层实现 我们知道,Python3.10中可以使用async和await关键字来实现原生协程函数的定义和调度,但其实,我们也可以利用生成器达到协程的效果,

  • python线程、进程和协程详解

    引言 解释器环境:python3.5.1 我们都知道python网络编程的两大必学模块socket和socketserver,其中的socketserver是一个支持IO多路复用和多线程.多进程的模块.一般我们在socketserver服务端代码中都会写这么一句: server = socketserver.ThreadingTCPServer(settings.IP_PORT, MyServer) ThreadingTCPServer这个类是一个支持多线程和TCP协议的socketserver

  • Python进阶之协程详解

    目录 协程 协程的应用场景 抢占式调度的缺点 用户态协同调度的优势 协程的运行原理 Python 中的协程 总结 协程 协程(co-routine,又称微线程)是一种多方协同的工作方式.当前执行者在某个时刻主动让出(yield)控制流,并记住自身当前的状态,以便在控制流返回时能从上次让出的位置恢复(resume)执行. 简而言之,协程的核心思想就在于执行者对控制流的 “主动让出” 和 “恢复”.相对于,线程此类的 “抢占式调度” 而言,协程是一种 “协作式调度” 方式. 协程的应用场景 抢占式调

  • python并发编程之多进程、多线程、异步和协程详解

    最近学习python并发,于是对多进程.多线程.异步和协程做了个总结. 一.多线程 多线程就是允许一个进程内存在多个控制权,以便让多个函数同时处于激活状态,从而让多个函数的操作同时运行.即使是单CPU的计算机,也可以通过不停地在不同线程的指令间切换,从而造成多线程同时运行的效果. 多线程相当于一个并发(concunrrency)系统.并发系统一般同时执行多个任务.如果多个任务可以共享资源,特别是同时写入某个变量的时候,就需要解决同步的问题,比如多线程火车售票系统:两个指令,一个指令检查票是否卖完

  • go语言中的协程详解

    协程的特点 1.该任务的业务代码主动要求切换,即主动让出执行权限 2.发生了IO,导致执行阻塞(使用channel让协程阻塞) 与线程本质的不同 C#.java中我们执行多个线程,是通过时间片切换来进行的,要知道进行切换,程序需要保存上下文等信息,是比较消耗性能的 GO语言中的协程,没有上面这种切换,一定是通过协程主动放出权限,不是被动的. 例如: C# 中创建两个线程 可以看到1和2是交替执行的 Go语言中用协程实现一下 runtime.GOMAXPROCS(1) 这个结果就是 执行了1 在执

  • python全栈要学什么 python全栈学习路线

    IT行业,技术要比学历.年龄.从业经验更为重要,技术水平直接决定就业薪资,想要学好python,首先要先了解精通Python语言基础.Python web开发.Python爬虫.Python数据分析这四大方面. 全栈即指的是全栈工程师,指掌握多种技能,并能利用多种技能独立完成产品的人.就是与这项技能有关的都会,都能够独立的完成. 全栈只是个概念,也分很多种类.真正的全栈工程师涵盖了web开发.DBA .爬虫 .测试.运维,要学的内容那是相当的巨量.就web开发方向而言需要学习的内容:前端知识 包

随机推荐