了解异步编程

楼主在工作中遇到了以下问题,开发接口爬取数据代码完成之后要写入redis缓存,但是在写入缓存的过程花费2-3s,进行这样就大大影响了接口的性能,于是想到了使用异步存储。

传统的同步编程是一种请求响应模型,调用一个方法,等待其响应返回.
异步编程就是要重新考虑是否需要响应的问题,也就是缩小需要响应的地方。因为越快获得响应,就是越同步化,顺序化,事务化,性能差化。

线程实现异步

思路:通过线程调用的方式,来达到异步非阻塞的效果,也就是说主程序无需等待线程执行完毕,仍然可以继续向下执行。

1.threading模块和thread模块

Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。

threading 模块提供的其他方法:

  • threading.currentThread(): 返回当前的线程变量。
  • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:

  • run(): 用以表示线程活动的方法。
  • start():启动线程活动。
  • join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
  • isAlive(): 返回线程是否活动的。
  • getName(): 返回线程名。
  • setName(): 设置线程名。

同步阻塞:

 import  threading,time

 def thead(num):
time.sleep(1)
print("阻塞程序%s开始执行"%num)
time.sleep(3)
print("阻塞程序%s执行完毕"%num) def main():
print("主方法开始执行") for i in range(1,3):
thead(i) print("主方法执行完毕")
return if __name__ == '__main__':
print(time.ctime())
num = main()
print("返回结果为%s"%num)
print(time.ctime())
Wed Nov 21 09:22:56 2018
主方法开始执行
阻塞程序1开始执行
阻塞程序1执行完毕
阻塞程序2开始执行
阻塞程序2执行完毕
主方法执行完毕
返回结果为None
Wed Nov 21 09:23:04 2018

异步,无需等待线程执行


import  threading,time

def thead(num):
# time.sleep(1)
print("线程%s开始执行"%num)
time.sleep(3)
print("线程%s执行完毕"%num) def main():
print("主方法开始执行") #创建2个线程
poll = []#线程池
for i in range(1,3):
thead_one = threading.Thread(target=thead, args=(i,))
poll.append(thead_one) #线程池添加线程
for n in poll:
n.start() #准备就绪,等待cpu执行 print("主方法执行完毕")
return if __name__ == '__main__':
print(time.ctime())
num = main()
print("返回结果为%s"%num)
print(time.ctime())

Wed Nov 21 09:48:00 2018
主方法开始执行
主方法执行完毕
返回结果为None
Wed Nov 21 09:48:00 2018
线程1开始执行
线程2开始执行
线程1执行完毕
线程2执行完毕

2.concurrent.futures模块

concurrent.futures模块实现了对threading(线程)multiprocessing(进程)的更高级的抽象,对编写线程池/进程池提供了直接的支持。

从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提供了ThreadPoolExecutorProcessPoolExecutor两个类,ThreadPoolExecutor和ProcessPoolExecutor继承了Executor,分别被用来创建线程池和进程池的代码。(暂时只介绍线程池的使用)

concurrent.futures模块的基础是Exectuor,Executor是一个抽象类,它不能被直接使用。但是它提供的两个子类ThreadPoolExecutor和ProcessPoolExecutor却是非常有用,顾名思义两者分别被用来创建线程池和进程池的代码。我们可以将相应的tasks直接放入线程池/进程池,不需要维护Queue来操心死锁的问题,线程池/进程池会自动帮我们调度。

Future这个概念你可以把它理解为一个在未来完成的操作,这是异步编程的基础,传统编程模式下比如我们操作queue.get的时候,在等待返回结果之前会产生阻塞,cpu不能让出来做其他事情,而Future的引入帮助我们在等待的这段时间可以完成其他的操作。

  • Future Objects:Future类封装了可调用的异步执行.Future 实例通过 Executor.submit()方法创建。

  • submit(fn, *args, **kwargs):调度可调用的fn,作为fn(args kwargs)执行,并返回一个表示可调用的执行的Future对象。
  • ThreadPoolExecutor:ThreadPoolExecutor是一个Executor的子类,它使用线程池来异步执行调用。

  • concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix=''):Executor子类,使用max_workers规格的线程池来执行异步调用。

在Flask应用中使用异步redis:

from flask import Flask
import time
from concurrent.futures import ThreadPoolExecutor executor = ThreadPoolExecutor()
app = Flask(__name__) @app.route('/')
def update_redis():
executor.submit(do_update)
return 'ok' def do_update():
time.sleep(3)
print('start update cache')
time.sleep(1)
print("end") if __name__ == '__main__':
app.run(debug=True)

“ok“在更新缓存前已经返回。

本文到这里就结束了,着重介绍了线程实现异步的方法。当然还有其他的方法,比如yied实现,还有asyncio模块,后续会继续更新异步编程的文章。

温馨提示

  • 本文代码是在python3.5版本测试运行。
  • 如果您对本文有疑问,请在评论部分留言,我会在最短时间回复。
  • 如果本文帮助了您,也请评论关注,作为对我的一份鼓励。
  • 如果您感觉我写的有问题,也请批评指正,我会尽量修改。
  • 本文为原创,转载请注明出处。

  

python线程实现异步任务的更多相关文章

  1. Python线程,进程,携程,I/O同步,异步

    只有本人能看懂的-Python线程,进程,携程,I/O同步,异步 举个栗子: 我想get三个url,先用普通的for循环 import requests from multiprocessing im ...

  2. python 线程、多线程

    复习进程知识: python:主进程,至少有一个主线程 启动一个新的子进程:Process,pool 给每一个进程设定一下执行的任务:传一个函数+函数的参数 如果是进程池:map函数:传入一个任务函数 ...

  3. python 线程 进程 协程 学习

    转载自大神博客:http://www.cnblogs.com/aylin/p/5601969.html 仅供学习使用···· python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件和 ...

  4. [译]Python中的异步IO:一个完整的演练

    原文:Async IO in Python: A Complete Walkthrough 原文作者: Brad Solomon 原文发布时间:2019年1月16日 翻译:Tacey Wong 翻译时 ...

  5. Python的Asyncore异步Socket模块及实现端口转发的例子

    Python的Asyncore异步Socket模块及实现端口转发的例子 Asyncore模块提供了以异步的方式写入套接字服务客户端和服务器的基础结构. 只有两种方式使一个程序在单处理器上实现" ...

  6. Python线程池与进程池

    Python线程池与进程池 前言 前面我们已经将线程并发编程与进程并行编程全部摸了个透,其实我第一次学习他们的时候感觉非常困难甚至是吃力.因为概念实在是太多了,各种锁,数据共享同步,各种方法等等让人十 ...

  7. Python Flask后端异步处理(二)

    在实际的应用场景中,如用户注册,用户输入了注册信息后,后端保存信息到数据库中,然后跳转至登录界面,这些操作用户需要等待的时间非常短,但是如果是有耗时任务,比如对输入的网址进行漏洞扫描,在后端处理就会花 ...

  8. python ---线程,进程,协程

    本章内容 线程 进程 协程 线程是最小的调度单位 进程是最小的管理单元 线程 多线程的特点: 线程的并发是利用cpu上下文切换 多线程的执行的顺序是无序的 多线程共享全局变量 线程是继承在进程里的,没 ...

  9. python——线程与多线程进阶

    之前我们已经学会如何在代码块中创建新的线程去执行我们要同步执行的多个任务,但是线程的世界远不止如此.接下来,我们要介绍的是整个threading模块.threading基于Java的线程模型设计.锁( ...

随机推荐

  1. Codeforces Round #480 (Div. 2)980C Posterized+分组类贪心

    传送门:http://codeforces.com/contest/980/problem/C 参考 题意:给定n个数字,每个数在0~256间,现在给至多连续k的数分为一组,给出字典序最小的答案. 思 ...

  2. js生成玫瑰图

    html: <canvas id="c" height="600" width="600"></canvas> js ...

  3. 【Nginx】四层负载均衡配置

    一.概述 二.配置 2.1 环境准备 2.2 安装及配置 1).下载Nginx 2).下载nginx_tcp_proxy_module 插件 3).编译Nginx 4).修改Nginx.conf配置文 ...

  4. 5分钟理解 SpringBoot 响应式的核心-Reactor

    目录 一.前言 二. Mono 与 Flux 构造器 三. 流计算 1. 缓冲 2. 过滤/提取 3. 转换 4. 合并 5. 合流 6. 累积 四.异常处理 五.线程调度 小结 参考阅读 一.前言 ...

  5. Scrapy框架的下载与安装

    一.下载scrapy 首先,如果安装了anaconda, 可以直接在terminal窗口中输入: conda install scrapy 在图示符中,输入y, 表示继续处理信息 二.验证 是否安装成 ...

  6. Nginx安装及详解

    Nginx简介: Nginx(发音engine x)专为性能优化而开发的开源软件,是HTTP.反向代理.邮件代理.TCP/UDP协议代理软件,由俄罗斯的作者Igor Sysoev开发,其最知名的优点是 ...

  7. Unity3D_04_GameObject,Component,Time,Input,Physics

    Unity3D是一个Component-Based的游戏引擎,并且为GamePlay Programmer提供了很多游戏性层上的支持. 1.可以在图形界面上设计动画状态转换的Animator. 2.可 ...

  8. 实战spring自定义属性(schema)

    关于spring自定义属性(schema) 在开发Dubbo应用的时候,我们会在xml中做以下类似的配置: <dubbo:application name="dubbo_service ...

  9. Centos第一次使用配置IP地址

    1.vim /etc/sysconfig/network-scripts/ifcfg-eth0   修改默认配置文件 TYPE=Ethernet BOOTPROTO=static #静态 可修改为[n ...

  10. SpringAop实现公共字段填充

    一.说明 项目中经常会有一些放在缓存中的公共字段需要进行填充,我们知道mybatis-plus很方便地可以实现公共字段填充.在这里我定义了一个字段填充的注解,当我们需要进行数据填充的时候只要在方法上打 ...