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 ...
随机推荐
- XML巩固
一.XML基础 1.XML区分大小写, 2.XML属性值必须有引号(单引双引均可) 3.XML必须有根元素 4.一些特殊字符的需要用实体引用来替换 < < 小于 > > 大于 ...
- Android进阶练习一
1.有序广播: https://github.com/wuzhuojun/BroadcastExample 说明: 在manifest.xml设置的广播是静态注册,只要软件安装在手机上广播就能够生效. ...
- Hibernate HQL基础 使用参数占位符
在HQL中有两种方法实现使用参数占用符 1.使用? 使用?设置参数占位符,之后通过setString()和setInteger()等方法为其赋值.如: Query query = session.cr ...
- Eclipse 乱码解决方案(UTF8 -- GBK)
UTF8 --> GBK; GBK --> UTF8 eclipse的中文乱码问题,一般不外乎是由操作系统平台编码的不一致导致,如Linux中默认的中文字体编码问UTF8, 而Wind ...
- 九、 合成(Composite)模式 --结构模式(Structural Pattern)
合成模式:有时又叫做部分-整体模式(Part-Whole).合成模式将对象组织到树结构中,可以用来描述整体与部分的关系.合成模式可以使客户端将单纯元素与复合元素同等看待. 合成模式分为安全式和透明式 ...
- hdu 3926 Hand in Hand
http://acm.hdu.edu.cn/showproblem.php?pid=3926 这道题是判断两个图是不是同构相似.只要判断图中环的个数和链的个数,和每个环的节点数和链的节点数是否相等. ...
- SVN客户端--TortoiseSVN使用说明(转)
TortoiseSVN是windows下其中一个非常优秀的SVN客户端工具.通过使用它,我们可以可视化的管理我们的版本库.不过由于它只是一个客户端,所以它不能对版本库进行权限管理. TortoiseS ...
- Android framework浅析[转]
Android系统从底向上一共分了4层,每一层都把底层实现封装,并暴露调用接口给上一层. 1. Linux内核(Linux Kernel) 1)Android运行在linux kernel 2.6之上 ...
- B-JUI(Best jQuery UI) 前端框架
From :http://b-jui.com/大略看了下还不错,收藏之.
- Android.mk文件语法规范及使用模板
Android.mk文件语法详述 介绍:------------这篇文档是用来描述你的C或C++源文件中Android.mk编译文件的语法的,为了理解她们我们需要您先看完docs/OVERVIEW.h ...