python运维开发(十)----IO多路复用线程基本使用
内容目录:
- python作用域
- python2.7和python3.5的多继承区别
- IO多路复用
- socketserver模块源分析
- 多线程、进程、协程
python作用域
python中无块级作用域
if 1 == 1:
name = 'jabe' print(name)
#可以正常输出jabe #在python中无块级作用域
#在c#或者java中是不能这样使用的,提示name未定义的
python中以函数为作用域
def func():
name = 'jbae'
func()
print(name) #会提示name为定义的报错,说明在python中以函数为作用域的
python作用域链由内想外找,直到找不到报错
name = 'jabe'
def f1():
# name = 'a'
def f2():
# name = 'b'
print(name)
f2()
f1() #输出
jabe
函数执行前,作用域(链)已经确定
name = 'jabe' def f1():
print(name) def f2():
name = 'ljb'
f1() f2()
输出:
jabe
#===============================
name = 'jabe' def f1():
print(name) def f2():
name = 'ljb'
return f1 ret = f2()
ret()
输出:
jabe
lambda函数作用域
li = [lambda :x for x in range(10)]
r = li[0]()
print(r)
print('===================')
#输出为9,上面的例子等同于下面的代码 li = []
for i in range(10):
def f1():
return i
li.append(f1)
#li 为列表,内部的元素为相同功能的函数
print(li[0]())
print(li[1]())
print(li[2]())
print(li[3]()) #输出:
# 9
# ===================
# 9
# 9
# 9
# 9
由于函数为被调用时是未被执行的,所以当循环结束时i为9,最后调用时候显示均为9
上面的函数我们稍作更改就可以打印的不为9了
li = []
for i in range(10):
def f1(x=i):
return x
li.append(f1) #li 为列表,内部的元素为相同功能的函数 print(li[0]())
print(li[1]())
print(li[2]())
print(li[3]()) #输出:
# 0
# 1
# 2
# 3
上面的例子中因为每次执行循环,将i值赋给x,因此打印的为0,1,2,3
python2.7和python3.5的多继承区别
我们从以前学过的知识中可以知道,python3.5类的多重继承顺序为从左到右,由下到上的顺序来继承寻找的。
在python2.7中,如果为新式类和py3.5中是寻找的顺序是一样的,但是经典类的继承顺序稍有不同,一直深层寻找,直到找到为止,不会向右边寻找。
不继承object是经典类,继承object是新式类 (和py3中一致)
IO多路复用
I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
python中有一个select模块,提供了select,poll,epoll三个方法
select:适用多平台,win+linux都可以,inputs 最多监控1024个对象,底层for 循环,效率不高
poll:适用多平台,inputs最多监控对象个数无限制,底层和select 一样都为for循环
epoll:适用于linux平台,底层采用触发机制,发生变化时通知,非循环遍历,效率要比select和poll要高很多
select方法说明:
句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间) 参数: 可接受四个参数(前三个必须)
返回值:三个列表 select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。
利用select 实现伪并发,同时处理多个客户端的请求:服务端
import socket
import select sk = socket.socket()
sk.bind(('127.0.0.1', 9999,))
sk.listen(5)
inputs = [sk,]
outputs = []
messages = {} while True:
rlist, wlist, elist = select.select(inputs,outputs,[sk],1)
print(len(inputs),len(rlist),len(outputs),len(wlist))
# 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为【sk】
# 监听conn对象,如果conn发生变化,表示客户端有新消息发送过来了,此时rlist的值为 【客户端】
for r in rlist:
if r == sk:# 新客户来连接
conn,address = r.accept()# conn是什么?其实socket对象
inputs.append(conn)
messages[conn] = []
conn.sendall(bytes('Hello',encoding='utf-8'))
else: # 有人给我发消息了
print('====================')
try:
ret = r.recv(1024)
# r.sendall(ret)
if not ret:
raise Exception('断开连接')
else:
outputs.append(r)
messages[r].append(ret)
except Exception as e:
inputs.remove(r)
del messages[r] for w in wlist:# 所有给我发过消息的人
msg = messages[w].pop()
resp = msg + bytes('response', encoding='utf-8')
w.sendall(resp)
outputs.remove(w)
利用select 实现伪并发,同时处理多个客户端的请求:客户端
import socket
sk = socket.socket()
ip_port = ('127.0.0.1',9999,)
sk.connect(ip_port)
data = sk.recv(1024)
print(data.decode())
while True:
inp = input(">>>")
sk.sendall(bytes(inp,encoding='utf-8'))
print(sk.recv(1024).decode()) sk.close()
通过上面的实例我们可以知道通过IO多路复用可以实现读写的分离,其实IO多路复用,并不是真正的多并发处理,只是底层循环接收消息,接收到进行处理的,不是同时接收多并发处理的,所以上面的例子称之为伪并发。真正的socketserver实现的了真正的并发,其他socketserver中还用的多线程、进程的使用,所以我们也可以通过多线程方式加上这种伪并发来实现socketserver的多并发的功能。
socketserver模块源分析
SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。

源码分析
我们通过一个简单例子来分析执行过程:
import socketserver
class MyClass(socketserver.BaseRequestHandler):
    def handle(self):
        pass
obj = socketserver.ThreadingTCPServer(('127.0.0.1', 9999), MyClass)
obj.serve_forever() 
socketserver 调用过程如下图:

内部调用流程为:
- 启动服务端程序
- 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
- 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给self.RequestHandlerClass
- 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
- 当客户端连接到达服务器
- 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
- 执行 ThreadingMixIn.process_request_thread 方法
- 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
多线程、进程、协程
通过前面的学习我们可以知道,创建socket对象方式进行通讯默认是单进程单线程工作方式,而socketserver是通过多进程多线程的方式,在多线程中我们引用threding的模块。
threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。
thread简单实例:
import threading
import time def f1(arg):
time.sleep(2)
print(arg) for i in range(10):
t1 = threading.Thread(target=f1,args=(i,))
t1.start()
上述代码创建了10个“前台”线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。
thread的其他方法:
- start 线程准备就绪,等待CPU调度
- setName 为线程设置名称
- getName 获取线程名称
- setDaemon 设置为后台线程或前台线程(默认)
- 如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
- 如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
- join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
- run 线程被cpu调度后自动执行线程对象的run方法
import threading
import time def f1(arg):
time.sleep(2)
print(arg) # for i in range(10):
# t1 = threading.Thread(target=f1,args=(i,))
# t1.start() t1 = threading.Thread(target=f1,args=(123,))
t1.setDaemon(True) #默认为False,设置为True表示主线程不等子线程
t1.start() #不代表当前线程会被立即执行
t1.join(1) #表示主线程到此等待。。。直到子线程执行完毕,参数表示主线程最多等待n秒 print('end')
python运维开发(十)----IO多路复用线程基本使用的更多相关文章
- python运维开发(十八)----Django(二)
		内容目录 路由系统 模版 Ajax model数据库操作,ORM 路由系统 django中的路由系统和其他语言的框架有所不同,在django中每一个请求的url都要有一条路由映射,这样才能将请求交给对 ... 
- python运维开发(十五)----JavaScript
		内容目录: HTML补充 javascript HTML补充 1.display标签 display的inline-block 属性会自动带3px的宽度 <span style="di ... 
- python运维开发(十二)----rabbitMQ、pymysql、SQLAlchemy
		内容目录: rabbitMQ python操作mysql,pymysql模块 Python ORM框架,SQLAchemy模块 Paramiko 其他with上下文切换 rabbitMQ Rabbit ... 
- python运维开发(十六)----Dom&&jQuery
		内容目录: Dom 查找 操作 事件 jQuery 查找 筛选 操作 事件 扩展 Dom 文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文档的编程接口.它 ... 
- python运维开发(十四)----HTML基本操作
		内容目录: HTML概述 head标签 body中常用标签 css选择器 css常用属性 HTML HTML概述 HTML是英文Hyper Text Mark-up Language(超文本标记语言) ... 
- python运维开发(十九)----Django后台表单验证、session、cookie、model操作
		内容目录: Django后台表单验证 CSRF加密传输 session.cookie model数据库操作 Django后台Form表单验证 Django中Form一般有2种功能: 1.用于做用户提交 ... 
- Python运维开发基础09-函数基础【转】
		上节作业回顾 #!/usr/bin/env python3 # -*- coding:utf-8 -*- # author:Mr.chen # 实现简单的shell命令sed的替换功能 import ... 
- Python运维开发基础07-文件基础【转】
		一,文件的基础操作 对文件操作的流程 [x] :打开文件,得到文件句柄并赋值给一个变量 [x] :通过句柄对文件进行操作 [x] :关闭文件 创建初始操作模板文件 [root@localhost sc ... 
- Python运维开发基础10-函数基础【转】
		一,函数的非固定参数 1.1 默认参数 在定义形参的时候,提前给形参赋一个固定的值. #代码演示: def test(x,y=2): #形参里有一个默认参数 print (x) print (y) t ... 
随机推荐
- [TYVJ] P1065 津津的储蓄计划
			津津的储蓄计划 背景 Background NOIP2004 提高组 第一道 描述 Description 津津的零花钱一直都是自己管理.每个月的月初妈妈给津津300元钱,津津会预算这个月 ... 
- Android Service 通过 BroadcastReceiver 更新Activity UI
			1:MainActivity.java public class MainActivity extends Activity { private TextView tvInfo = null; pri ... 
- Spring3.0.5  获取表中自增的主键(mysql)
			public int addWsstxCotent(final WsstxContent wsstxContent) { final String sql = "insert into ws ... 
- 【转】高通平台android 环境配置编译及开发经验总结
			原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通 ... 
- poj1833 排列
			... 
- nginx编译配置
			1, 正向代理是一个位于内网客户端和外网原始服务器之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标,然后由代理服务器向 原始服务器转交请求并将获得的内容返回给客户端.正向代理 ... 
- SVN:冲突解决 合并别人的修改
			在项目中,基本不可避免多个人同时参与一个项目,因此就可能会出现多个人同时修改一个文件的情况,就不可避免的会出现冲突.svn已经很聪明了,如 果你和别人对于同一个文件的修改之间不存在重叠(比如你在文件最 ... 
- sizeof用法
			c语言详解sizeof 原文地址:http://blog.sina.com.cn/s/blog_5da08c340100bmwu.html 一.sizeof的概念 sizeof是C语言的一种单 ... 
- Java 强引用,软引用,弱引用
			1.强引用 public void handleMessage(Message msg) { case FAIL: GoplayException mException = new GoplayExc ... 
- struts2的<constant/>标签使用
			<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-/ ... 
