【python自动化第十一篇:】

课程简介

  • gevent协程
  • select/poll/epoll/异步IO/事件驱动
  • RabbitMQ队列

上节课回顾

  • 进程:

    • 进程的诞生时为了处理多任务,资源的隔离,提供程序运行的所有数据
    • 进程就是一个程序运行所需要的资源集合
    • 每个进程的数据是独立的
    • 每个进程至少有一个线程
    • 适用于CPU密集型程序(金融分析等。。)
  • 线程:

    • 线程数据是共享的
    • 线程依赖于进程运行
    • 适用于IO密集型程序(socket,web,爬虫)
  • 总结:

    • 一个进程的多个线程可以充分利用多和cpu
    • 进程间数据共享的代价是高昂的,所以要尽量避免进程间的数据共享
    • 线程要修改同一份数据,必须要加锁(互斥锁mutex)
    • event :线程间交互
  • 多进程

    • multiprocessing

      • pipe
      • queue :FIFO,LIFO,priority
      • pipe和queue实现的是进程间的数据传递和通信
      • manager实现了多进程间的数据共享
  • 生产者消费者模型

    • 解耦(降低程序之间的关联)
    • 提高程序的运行效率

协程

协程:又称微线程,是一种用户态的轻量级线程

1 . 拥有自己的寄存器上下文和栈,协程调度切换时将寄存器上下文和栈保存到其他的地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。

2 . 协程能够保存上一次调用时的状态(所有局部状态的一个特定组合),每一次过程重入时,就相当于进入上一次调用的状态,换一种说法就是进入上一次离开时所处的逻辑流的位置

  • 协程的好处:
  1. 无需上下文切换的开销
  2. 无需原子操作锁定以及同步开销
  3. 方便切换控制流,简化编程模型
  4. 高并发+高拓展性+低成本:一个CPU支持上万个协程都不是问题,所以很适合高并发处理
  • 协程的缺点:
  1. 无法利用多核资源
  2. 进行阻塞操作(如IO)会阻塞整个程序
  • 小例子:
  1. import queue
  2. import time
  3. def consumer(name):
  4. """
  5. 定义消费者
  6. :param name:
  7. :return:
  8. """
  9. print("-->starting eating baozi!!")
  10. while True:
  11. new_baozi = yield
  12. print("[%s] is eating baozi %s"%(name,new_baozi))
  13. time.sleep(1) #程序中间有停顿,则会导致程序串行执行
  14. def producer():
  15. """
  16. 定义生产者
  17. :return:
  18. """
  19. r = con.__next__()
  20. r = con2.__next__()
  21. n = 0
  22. while n<5000000000: #循环5000个包子
  23. n+=1
  24. con.send(n)
  25. con2.send(n)
  26. # print("\033[32;1m[producer]\033[0m is making baozi %s"%n)
  27. if __name__ == '__main__':
  28. con = consumer("c1")
  29. con2 = consumer("c2")
  30. p = producer()

总结:

协程从上述程序中得出的定义:

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需要加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 一个协程遇到其他的IO操作就自动切换到其他协程
  • 通过greenlet模块实现的协程
  1. from greenlet import greenlet
  2. import time
  3. def test1():
  4. print(12)
  5. time.sleep(2) #等待之后再切换
  6. gr2.switch() #切换到gr2
  7. print(34)
  8. gr2.switch()
  9. def test2():
  10. print(56)
  11. gr1.switch()
  12. print(78)
  13. gr1 = greenlet(test1)
  14. gr2 = greenlet(test2)
  15. gr1.switch()

不用变成生成器,可以在任意位置切换,但是不能够实现 一个协程遇到其他的IO操作就自动切换到其他协程

  • gevent实现并发同步或者异步编程
  1. import gevent
  2. def func1():
  3. print('\033[31;1mfunc1开始运行\033[0m')
  4. gevent.sleep(2)
  5. print("\033[31;1mfunc1继续运行\033[0m")
  6. def func2():
  7. print('\033[32;1mfunc2开始运行\033[0m')
  8. gevent.sleep(1)
  9. print("\033[32;1mfunc2继续运行\033[0m")
  10. def func3():
  11. print("333333")
  12. gevent.sleep(1)
  13. print("44444")
  14. gevent.joinall([
  15. gevent.spawn(func1),
  16. gevent.spawn(func2),
  17. gevent.spawn(func3)
  18. ])

输出结果如下:

func1开始运行

func2开始运行

333333

func2继续运行

44444

func1继续运行

  • 同步和异步的性能差异对比
  1. import gevent
  2. def task(pid):
  3. gevent.sleep(0.5)
  4. print("task %s done"%pid)
  5. def synchronous():
  6. for i in range(1,10):
  7. task(i)
  8. def asynchronous():
  9. #threads = [gevent.spawn(task,i) for i in range(10)] #列表生成式
  10. threads = []
  11. for i in range(10):
  12. threads.append(gevent.spawn(task,i))
  13. gevent.joinall(threads)
  14. print("synchonous")
  15. synchronous() #串行(同步)
  16. # print("asynchonoous..")
  17. # asynchronous() #并行(异步)
  • 并发爬虫实例
  1. import gevent
  2. from gevent import monkey
  3. monkey.patch_all() #打一发补丁
  4. from urllib.request import urlopen
  5. import time
  6. def pa_web_page(url):
  7. print("starting get url ",url)
  8. req = urlopen(url) #打开链接
  9. data = req.read() #读取数据
  10. print(data)
  11. print("%d bytes recieved from %s"%(len(data),url))
  12. # start_time = time.time()
  13. # pa_web_page("http://www.xiaohuar.com")
  14. # pa_web_page("http://www.autohome.com.cn/beijing/")
  15. # print("cost time is %s"%(time.time()-start_time))
  16. start_time2 = time.time()
  17. gevent.joinall([
  18. gevent.spawn(pa_web_page,"http://www.xiaohuar.com"),
  19. gevent.spawn(pa_web_page,"http://www.autohome.com.cn/beijing/"),
  20. gevent.spawn(pa_web_page,"http://www.sina.com"),
  21. ])
  22. print("cost time is %s"%(time.time()-start_time2))
  • 基于socket的并发连接测试

    (1).socket_server 代码如下:
  1. import socket
  2. import gevent
  3. from gevent import monkey
  4. monkey.patch_all()
  5. def server(port):
  6. s = socket.socket()
  7. s.bind(("0.0.0.0",port))
  8. s.listen(500)
  9. while True:
  10. client,addr=s.accept()
  11. gevent.spawn(handle_request,client)
  12. def handle_request(conn):
  13. try:
  14. while True:
  15. data = conn.recv(1024)
  16. print("recv",data)
  17. conn.send(data)
  18. if not data:
  19. conn.shutdown(socket.SHUT_WR)
  20. except Exception as e:
  21. print(e)
  22. finally:
  23. conn.close()
  24. if __name__ == '__main__':
  25. server(8888)

(2). socket_client for threading 代码如下:

  1. import socket
  2. import threading
  3. def sock_conn():
  4. client = socket.socket()
  5. client.connect(("localhost",8888))
  6. count = 0
  7. while True:
  8. client.send(("hello %s"%count).encode("utf-8"))
  9. data = client.recv(1024)
  10. print("[%s] recv server %s"%(threading.get_ident(),data.decode()))
  11. count+=1
  12. client.close()
  13. for i in range(100): #测试并发连接个数
  14. t = threading.Thread(target=sock_conn)
  15. t.start()
  • 此时会有一个弊端就是当并发高于1024就会不稳定

    (3). socket_client normal 代码如下:
  1. import socket
  2. HOST = "localhost"
  3. PORT = 8888
  4. c = socket.socket()
  5. c.connect((HOST,PORT))
  6. while True:
  7. msg = bytes(input(">>"),encoding="utf-8")
  8. c.sendall(msg)
  9. data = c.recv(1024)
  10. print("Recived",repr(data))

事件驱动和异步IO

  • 通常我们写服务器处理模型的程序时,遇到的模型:

    (1)每收到一个请求,创建一个新的进程,来处理该请求;

    (2)每收到一个请求,创建一个新的线程,来处理该请求;

    (3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求

    特点:

    第(1)中方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。

    第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。

    第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。

    综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式

  • 事件驱动类型介绍:

    在UI编程中,常常要对鼠标点击进行相应,首先如何获得鼠标点击呢?

    方式一:创建一个线程,该线程一直循环检测是否有鼠标点击,那么这个方式有以下几个缺点:

  1. CPU资源浪费,可能鼠标点击的频率非常小,但是扫描线程还是会一直循环检测,这会造成很多的CPU资源浪费;如果扫描鼠标点击的接口是阻塞的呢?

  2. 如果是堵塞的,又会出现下面这样的问题,如果我们不但要扫描鼠标点击,还要扫描键盘是否按下,由于扫描鼠标时被堵塞了,那么可能永远不会去扫描键盘;

  3. 如果一个循环需要扫描的设备非常多,这又会引来响应时间的问题;

    所以,该方式是非常不好的。

    方式二:就是事件驱动模型

    目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:

  4. 有一个事件(消息)队列;

  5. 鼠标按下时,往这个队列中增加一个点击事件(消息);

  6. 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;

  7. 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;

  1. echo 'Hello world!';

【python自动化第十一篇】的更多相关文章

  1. python自动化运维篇

    1-1 Python运维-课程简介及基础 1-2 Python运维-自动化运维脚本编写 2-1 Python自动化运维-Ansible教程-Ansible介绍 2-2 Python自动化运维-Ansi ...

  2. Appium+python自动化(十一)- 元素定位秘籍助你打通任督二脉 - 下卷(超详解)

    简介 宏哥看你骨骼惊奇,印堂发亮,必是练武之奇才! 按照上一篇的节目预告,这一篇还是继续由宏哥给小伙伴们分享元素定位,是不是按照上一篇的秘籍修炼,是不是感觉到头顶盖好像被掀开,内气从头上冒出去,顿时觉 ...

  3. Python自动化 【第九篇】:Python基础-线程、进程及python GIL全局解释器锁

    本节内容: 进程与线程区别 线程 a)  语法 b)  join c)  线程锁之Lock\Rlock\信号量 d)  将线程变为守护进程 e)  Event事件 f)   queue队列 g)  生 ...

  4. Python自动化 【第二篇】:Python基础-列表、元组、字典

    本节内容 模块初识 .pyc简介 数据类型初识 数据运算 列表.元组操作 字符串操作 字典操作 集合操作 字符编码与转码 一.模块初识 Python的强大之处在于他有非常丰富和强大的标准库和第三方库, ...

  5. Python自动化 【第一篇】:Python简介和入门

    Python简介: 一.什么是python Python是一门动态解释性的强类型定义语言. pythonde 特点:“优雅”.“明确”.“简单”. 二.Python由来 python的创始人为吉多·范 ...

  6. 【python自动化第十篇:】

    复习: 线程与进程的区别: 进程:资源的集合 线程:最小调度单位 进程至少包含一个线程 线程之间的内存是共享的,两个线程操作同一个数据就会修改整个结果(需要mutex加锁来保持数据的一致性),递归锁, ...

  7. 【python自动化第八篇:网络编程】

    一.拾遗 动态导入模块 目的是为了在导入模块的过程中将模块以字符的格式导入. #!/usr/bin/env python # -*- coding:utf-8 -*- #Author:wanghui ...

  8. 【python自动化第七篇:面向对象进阶】

    知识点概览: 静态方法,类方法,属性方法 类的特殊方法 反射 异常处理 socket开发基础 一.静态方法:@staticmethod 只是名义上归类管理,实际上在静态方法里访问不了类或者实例中的任何 ...

  9. 【python自动化第六篇:面向对象】

    知识点概览: 面向对象的介绍 面向对象的特性(class,object)实例变量,类变量 面型对象编程的介绍 其他概念 一.面向对象介绍 编程范式:面向对象,面向过程,函数式编程 面向过程:通过一组指 ...

随机推荐

  1. Fast CGI 工作原理

    http://www.cppblog.com/woaidongmao/archive/2011/06/21/149092.html 一.FastCGI是什么? FastCGI是语言无关的.可伸缩架构的 ...

  2. easyui源码翻译1.32--Tree(树)

    前言 使用$.fn.tree.defaults重写默认值对象.下载该插件翻译源码 树控件在web页面中一个将分层数据以树形结构进行显示.它提供用户展开.折叠.拖拽.编辑和异步加载等功能. 源码 /** ...

  3. 第九章 Mass Storage设备

    9.1 Mass Storage设备介绍 USB的Mass Storage类是USB大容量储存设备类(Mass Storage Device Class).专门用于大容量存储设备,比如U盘.移动硬盘. ...

  4. Navicat 导入导出

    当我们对mysql数据库进行了误操作,造成某个数据表中的部分数据丢失时,肯定就要利用备份的数据库,对丢失部分的数据进行导出.导入操作了.Navicat工具正好给我们提供了一个数据表的导入导出功能. 1 ...

  5. EditPlus 配置 Java & C/CPP 开发环境

    0.1安装EditPlus 0.2安装Java 0.3安装MinGW 0.4配置Java和MinGW环境变量 1.配置Java开发环境 1.1 Tool-->Preferences 1.2 Ja ...

  6. BZOJ_1607_ [Usaco2008_Dec]_Patting_Heads_轻拍牛头_(筛数)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1607 给出一组n个数,求每个数能被多少个其他的数整除. 分析 暴力一点的做法就是每个数去筛它的 ...

  7. poj 1184 广搜进阶题

    起初的想法果然就是一个6000000的状态的表示. 但是后面觉得还是太过于幼稚了. 可以看看网上的解释,其实就是先转换位置,然后再改变数字的大小. #include<iostream> # ...

  8. 1.进入debug模式(基础知识列表)

    1.进入debug模式(基础知识列表)1.设置断点 2.启动servers端的debug模式 3.运行程序,在后台遇到断点时,进入debug调试状态 ========================= ...

  9. HDU5673 Robot 默慈金数

    分析: 注:然后学了一发线性筛逆元的姿势 链接:http://blog.miskcoo.com/2014/09/linear-find-all-invert #include<iostream& ...

  10. UVA 11426 GCD - Extreme (II) 欧拉函数

    分析:枚举每个数的贡献,欧拉函数筛法 #include <cstdio> #include <iostream> #include <ctime> #include ...