大家好,并发编程 进入第十一章。

前面两节,我们讲了协程中的单任务和多任务

这节我们将通过一个小实战,来对这些内容进行巩固。


在实战中,将会用到以下知识点:

  • 多线程的基本使用
  • Queue消息队列的使用
  • Redis的基本使用
  • asyncio的使用

. 动态添加协程

在实战之前,我们要先了解下在asyncio中如何将协程态添加到事件循环中的。这是前提。

如何实现呢,有两种方法:

  • 主线程是同步的
import time
import asyncio
from queue import Queue
from threading import Thread def start_loop(loop):
# 一个在后台永远运行的事件循环
asyncio.set_event_loop(loop)
loop.run_forever() def do_sleep(x, queue, msg=""):
time.sleep(x)
queue.put(msg) queue = Queue() new_loop = asyncio.new_event_loop() # 定义一个线程,并传入一个事件循环对象
t = Thread(target=start_loop, args=(new_loop,))
t.start() print(time.ctime()) # 动态添加两个协程
# 这种方法,在主线程是同步的
new_loop.call_soon_threadsafe(do_sleep, 6, queue, "第一个")
new_loop.call_soon_threadsafe(do_sleep, 3, queue, "第二个") while True:
msg = queue.get()
print("{} 协程运行完..".format(msg))
print(time.ctime())

由于是同步的,所以总共耗时6+3=9秒.

输出结果

Thu May 31 22:11:16 2018
第一个 协程运行完..
Thu May 31 22:11:22 2018
第二个 协程运行完..
Thu May 31 22:11:25 2018
  • 主线程是异步的,这是重点,一定要掌握。。
import time
import asyncio
from queue import Queue
from threading import Thread def start_loop(loop):
# 一个在后台永远运行的事件循环
asyncio.set_event_loop(loop)
loop.run_forever() async def do_sleep(x, queue, msg=""):
await asyncio.sleep(x)
queue.put(msg) queue = Queue() new_loop = asyncio.new_event_loop() # 定义一个线程,并传入一个事件循环对象
t = Thread(target=start_loop, args=(new_loop,))
t.start() print(time.ctime()) # 动态添加两个协程
# 这种方法,在主线程是异步的
asyncio.run_coroutine_threadsafe(do_sleep(6, queue, "第一个"), new_loop)
asyncio.run_coroutine_threadsafe(do_sleep(3, queue, "第二个"), new_loop) while True:
msg = queue.get()
print("{} 协程运行完..".format(msg))
print(time.ctime())

输出结果

由于是同步的,所以总共耗时max(6, 3)=6秒

Thu May 31 22:23:35 2018
第二个 协程运行完..
Thu May 31 22:23:38 2018
第一个 协程运行完..
Thu May 31 22:23:41 2018

. 实战:利用redis实现动态添加任务

对于并发任务,通常是用生成消费模型,对队列的处理可以使用类似master-worker的方式,master主要用户获取队列的msg,worker用户处理消息。

为了简单起见,并且协程更适合单线程的方式,我们的主线程用来监听队列,子线程用于处理队列。这里使用redis的队列。主线程中有一个是无限循环,用户消费队列。

先安装Redis
到 https://github.com/MicrosoftArchive/redis/releases 下载

解压到你的路径。

然后,在当前路径运行cmd,运行redis的服务端。

服务开启后,我们就可以运行我们的客户端了。
并依次输入key=queue,value=5,3,1的消息。

一切准备就绪之后,我们就可以运行我们的代码了。

import time
import redis
import asyncio
from queue import Queue
from threading import Thread def start_loop(loop):
# 一个在后台永远运行的事件循环
asyncio.set_event_loop(loop)
loop.run_forever() async def do_sleep(x, queue):
await asyncio.sleep(x)
queue.put("ok") def get_redis():
connection_pool = redis.ConnectionPool(host='127.0.0.1', db=0)
return redis.Redis(connection_pool=connection_pool) def consumer():
while True:
task = rcon.rpop("queue")
if not task:
time.sleep(1)
continue
asyncio.run_coroutine_threadsafe(do_sleep(int(task), queue), new_loop) if __name__ == '__main__':
print(time.ctime())
new_loop = asyncio.new_event_loop() # 定义一个线程,运行一个事件循环对象,用于实时接收新任务
loop_thread = Thread(target=start_loop, args=(new_loop,))
loop_thread.setDaemon(True)
loop_thread.start()
# 创建redis连接
rcon = get_redis() queue = Queue() # 子线程:用于消费队列消息,并实时往事件对象容器中添加新任务
consumer_thread = Thread(target=consumer)
consumer_thread.setDaemon(True)
consumer_thread.start() while True:
msg = queue.get()
print("协程运行完..")
print("当前时间:", time.ctime())

稍微讲下代码

loop_thread:单独的线程,运行着一个事件对象容器,用于实时接收新任务。
consumer_thread:单独的线程,实时接收来自Redis的消息队列,并实时往事件对象容器中添加新任务。

输出结果

Thu May 31 23:42:48 2018
协程运行完..
当前时间: Thu May 31 23:42:49 2018 协程运行完..
当前时间: Thu May 31 23:42:51 2018 协程运行完..
当前时间: Thu May 31 23:42:53 2018

我们在Redis,分别发起了5s,3s,1s的任务。
从结果来看,这三个任务,确实是并发执行的,1s的任务最先结束,三个任务完成总耗时5s

运行后,程序是一直运行在后台的,我们每一次在Redis中输入新值,都会触发新任务的执行。。


好了,经过这个实战内容,你应该对asyncio的实际应用有了一个更深刻的认识了,至此,你已经可以使用asyncio来实现任务的并发。快去体验一下。如果有什么疑问,请在后台加我微信与我联系。。

Python并发编程之实战异步IO框架:asyncio 下篇(十一)的更多相关文章

  1. Python并发编程之初识异步IO框架:asyncio 上篇(九)

    大家好,并发编程 进入第九篇. 通过前两节的铺垫(关于协程的使用),今天我们终于可以来介绍我们整个系列的重点 -- asyncio. asyncio是Python 3.4版本引入的标准库,直接内置了对 ...

  2. Python并发编程之学习异步IO框架:asyncio 中篇(十)

    大家好,并发编程 进入第十章.好了,今天的内容其实还挺多的,我准备了三天,到今天才整理完毕.希望大家看完,有所收获的,能给小明一个赞.这就是对小明最大的鼓励了.为了更好地衔接这一节,我们先来回顾一下上 ...

  3. Python并发编程之同步\异步and阻塞\非阻塞

    一.什么是进程 进程: 正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 进程和程序的区别: 程序仅仅只是一堆代码而已,而进程指的是程序的运行过程. 需要强调的是:同一个程序执行两次,那也 ...

  4. Python并发编程二(多线程、协程、IO模型)

    1.python并发编程之多线程(理论) 1.1线程概念 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程(流水线的工作需要电源,电源就相当于 ...

  5. python 并发编程 io模型 目录

    python 并发编程 IO模型介绍 python 并发编程 socket 服务端 客户端 阻塞io行为 python 并发编程 阻塞IO模型 python 并发编程 非阻塞IO模型 python 并 ...

  6. Python并发编程06 /阻塞、异步调用/同步调用、异步回调函数、线程queue、事件event、协程

    Python并发编程06 /阻塞.异步调用/同步调用.异步回调函数.线程queue.事件event.协程 目录 Python并发编程06 /阻塞.异步调用/同步调用.异步回调函数.线程queue.事件 ...

  7. Python并发编程05 /死锁现象、递归锁、信号量、GIL锁、计算密集型/IO密集型效率验证、进程池/线程池

    Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密集型效率验证.进程池/线程池 目录 Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密 ...

  8. Python并发编程内容回顾

    Python并发编程内容回顾 并发编程小结 目录 • 一.到底什么是线程?什么是进程? • 二.Python多线程情况下: • 三.Python多进程的情况下: • 四.为什么有这把GIL锁? • 五 ...

  9. python并发编程之线程/协程

    python并发编程之线程/协程 part 4: 异步阻塞例子与生产者消费者模型 同步阻塞 调用函数必须等待结果\cpu没工作input sleep recv accept connect get 同 ...

随机推荐

  1. connector for python实验

    MySQL 是最流行的关系型数据库管理系统,如果你不熟悉 MySQL,可以阅读 MySQL 教程. 下面为大家介绍使用 mysql-connector 来连接使用 MySQL, mysql-conne ...

  2. es6的基本数据详解

    一.Set 基本用法:   1)ES6提供了新的数据机构-Set. 它类似于数组,但是成员的值都是唯一的,没有重复的值.Set本身是一个构造函数,用来生成Set数据结构. 先来看一段最简单的代码: 1 ...

  3. 编译部署 Mysql 5.7

    1.环境准备 RHEL7.4(最小化安装)  64bit   2G 内存 (1G 内存编译将近一个小时) 磁盘空间 15G 以上. 配置为本地yum 源 从MySQL5.7版本开始,安装MySQL需要 ...

  4. 2.Git配置和关联GitHub

    1.配置本地信息, 右键Git Bush Here git config –global user.name '账号名' ##回车 git config –global user.email 邮箱 # ...

  5. vue-router的学习

    一.路由的概述. vue-router是vue.js官方的路由插件,它和vue.js是深度集成的,适用于构建单页面.vue的单页面应用是基于路由和组件的,路由是用于设定访问路径,并将路径和组件映射起来 ...

  6. 关于postgresql group by 报错

    举个例子:     table name:makerar cname  | wmname |          avg            --------+-------------+------ ...

  7. mysql数据库表的修改及删除

    一.对数据表的修改 1.重命名一张表: RENAME TABLE 原名 TO 新名字; ALTER TABLE 原名 RENAME 新名; ALTER TABLE 原名 RENAME TO 新名; 2 ...

  8. 浅谈开发中python通过os模块存储数据

    #其实本人很烦发博客,但为了面试还是发一下好,证明一下自己的能力 前言 首先说一下适用环境,在开发中我们有一些经常用到的数据(数据量大)需要存储起来. 存sql嘛又不合适,要知道在开发中每条sql语句 ...

  9. 关于 Senparc.Weixin.Cache.Redis 引用的 StackExchange.Redis 版本不匹配的反馈测试

    推测原因是老系统中有地方引用了旧版本的 StackExchange.Redis,原因是 StackExchange.Redis 1.2.6 版本未提供针对 .net 4.6 以上的支持,导致库引用会失 ...

  10. Go语言反射reflect

    目录 通过反射获取类型信息 理解反射的类型(Type)与种类(Kind) reflect.Elem() - 通过反射获取指针指向的元素类型 通过反射获取结构体的成员类型 通过反射获取值信息 使用反射值 ...