Python学习第八天
SocketServer模块:
SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器端创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。
为了好理解,我们先看个例子吧:
1 import socketserver 2 class MyTCPHandler(socketserver.BaseRequestHandler): 3 def handle(self): 4 while True: 5 print("New Conn:",self.client_address) 6 data=self.request.recv(1024) 7 if not data:break 8 print("Client Says:",data.decode()) 9 self.request.send(data) 10 if __name__=='__main__': 11 HOST,PORT="localhost",50007 12 server=socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler) 13 server.serve_forever()
1 import socket 2 ip_port=('127.0.0.1',50007) 3 sk=socket.socket() 4 sk.connect(ip_port) 5 while True: 6 msg=input(">>:").strip() 7 sk.sendall(bytes(msg,'utf-8')) 8 server_reply=sk.recv(1024) 9 print("Server Reply:",str(server_reply,'utf-8')) 10 sk.close()
上面例子的效果是 客户端输入什么内容,然后服务器端就会回应什么内容。
异常处理:
(先看以下内容:)
一、isinstance(obj, cls)
检查是否obj是否是类 cls 的对象
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 6 isinstance(obj, Foo)
二、issubclass(sub, super)
检查sub类是否是 super 类的派生类
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 issubclass(Bar, Foo)
1、异常基础
在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是显示一个提示的页面。
1 try: 2 pass 3 except Exception as ex: 4 pass
需求:将用户输入的两个数字相加
1 while True: 2 num1=input('num1') 3 num2=input('num2') 4 a=range(10) 5 try: 6 num1=int(num1) 7 num2=int(num2) 8 result=num1+num2 9 a[11] 10 except ValueError as e: 11 print("Value err:",e) 12 except IndexError as e: 13 print("Index err:",e) 14 except BaseException as e: 15 print("出现异常,信息如下:",e) 16 print(e)
2、异常种类
python中的异常种类非常多,每个异常专门用于处理某一项异常!!!
1 dic = ["tutu", 'peony'] 2 try: 3 dic[10] 4 except IndexError, e: 5 print e
1 dic = {'k1':'v1'} 2 try: 3 dic['k20'] 4 except KeyError, e: 5 print e
1 s1 = 'hello' 2 try: 3 int(s1) 4 except ValueError, e: 5 print e
异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。
# 未捕获到异常,程序直接报错 s1 = 'hello' try: int(s1) except IndexError as e: print (e)
所以,写程序时需要考虑到try代码块中可能出现的任意异常,可以这样写:
1 s1 = 'hello' 2 try: 3 int(s1) 4 except IndexError as e: 5 print (e) 6 except KeyError as e: 7 print (e) 8 except ValueError as e: 9 print (e)
万能异常 在python的异常中,有一个万能异常:Exception,他可以捕获任意异常,即:
1 s1 = 'hello' 2 try: 3 int(s1) 4 except Exception as e: 5 print (e)
虽然有这个万能异常,其他异常也是不可以忽略的!对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。
1 s1 = 'hello' 2 try: 3 int(s1) 4 except KeyError as e: 5 print ('键错误') 6 except IndexError as e: 7 print ('索引错误') 8 except Exception as e: 9 print ('错误')
3、异常其他结构
try: # 主代码块 pass except KeyError as e: # 异常时,执行该块 pass else: # 主代码块执行完,执行该块 pass finally: # 无论异常与否,最终执行该块 pass
4、主动触发异常
try: raise Exception('错误了。。。') except Exception as e: print (e)
5、自定义异常
1 class MyException(Exception): 2 3 def __init__(self, msg): 4 self.message = msg 5 6 def __str__(self): 7 return self.message 8 9 try: 10 raise MyException('我的异常') 11 except MyException as e: 12 print (e)
6、断言
# assert 条件 assert 1 == 1 assert 1 == 2
1 class AlexException(Exception): 2 def __init__(self,msg): 3 self.message==msg 4 def __str__(self): 5 return self.message 6 a=1 7 try: 8 assert a==1 9 except AlexException as e: 10 print(e) 11 else: 12 print("hahahh else") 13 finally: 14 print("no matter right or wrong run this anyway!")
线程与进程:
线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程有2种调用方式,如下:
1 import threading 2 import time 3 4 def sayhi(num): #定义每个线程要运行的函数 5 6 print("running on number:%s" %num) 7 8 time.sleep(3) 9 10 if __name__ == '__main__': 11 12 t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例 13 t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例 14 15 t1.start() #启动线程 16 t2.start() #启动另一个线程 17 18 print(t1.getName()) #获取线程名 19 print(t2.getName())
1 import threading 2 import time 3 4 5 class MyThread(threading.Thread): 6 def __init__(self,num): 7 threading.Thread.__init__(self) 8 self.num = num 9 10 def run(self):#定义每个线程要运行的函数 11 12 print("running on number:%s" %self.num) 13 14 time.sleep(3) 15 16 if __name__ == '__main__': 17 18 t1 = MyThread(1) 19 t2 = MyThread(2) 20 t1.start() 21 t2.start()
更多方法:
- start 线程准备就绪,等待CPU调度
- setName 为线程设置名称
- getName 获取线程名称
- setDaemon 设置为后台线程或前台线程(默认)
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 - join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
- run 线程被cpu调度后执行Thread类对象的run方法
请看下面的实例:
1 import time 2 import threading 3 4 def run(n): 5 6 print('[%s]------running----\n' % n) 7 time.sleep(2) 8 print('--done--') 9 10 def main(): 11 for i in range(5): 12 t = threading.Thread(target=run,args=[i,]) 13 #time.sleep(1) 14 t.start() 15 t.join(1) 16 print('starting thread', t.getName()) 17 18 19 m = threading.Thread(target=main,args=[]) 20 m.setDaemon(True) #将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务 21 m.start() 22 #m.join(timeout=2) 23 print("---main thread done----")
线程锁
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。所以,可能出现如下问题:
1 import threading 2 import time 3 4 gl_num = 0 5 6 def show(arg): 7 global gl_num 8 time.sleep(1) 9 gl_num +=1 10 print (gl_num) 11 12 for i in range(10): 13 t = threading.Thread(target=show, args=(i,)) 14 t.start() 15 16 print ('main thread stop')
1 import threading 2 import time 3 4 gl_num = 0 5 6 lock = threading.RLock() 7 8 def Func(): 9 lock.acquire() 10 global gl_num 11 gl_num +=1 12 time.sleep(1) 13 print(gl_num) 14 lock.release() 15 16 for i in range(10): 17 t = threading.Thread(target=Func) 18 t.start() 19 print ('main thread stop')
RLock(递归锁)
说白了就是在一个大锁中还要再包含子锁 它的用处是避免两个线程在执行的时候被其他的插在中间执行了,而我们要的结果是它俩一起完成执行不被打扰。
1 import threading,time 2 3 def run1(): 4 print("grab the first part data") 5 lock.acquire() 6 global num 7 num +=1 8 lock.release() 9 return num 10 def run2(): 11 print("grab the second part data") 12 lock.acquire() 13 global num2 14 num2+=1 15 lock.release() 16 return num2 17 def run3(): 18 lock.acquire() 19 res = run1() 20 print('--------between run1 and run2-----') 21 res2 = run2() 22 lock.release() 23 print(res,res2) 24 25 26 if __name__ == '__main__': 27 28 num,num2 = 0,0 29 lock = threading.RLock() 30 for i in range(10): 31 t = threading.Thread(target=run3) 32 t.start() 33 34 while threading.active_count() != 1: 35 print(threading.active_count()) 36 else: 37 print('----all threads done---') 38 print(num,num2)
Semaphore(信号量)
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 。
1 import threading,time 2 3 def run(n): 4 semaphore.acquire() 5 time.sleep(1) 6 print("run the thread: %s\n" %n) 7 semaphore.release() 8 9 if __name__ == '__main__': 10 11 num= 0 12 semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行 13 for i in range(20): 14 t = threading.Thread(target=run,args=(i,)) 15 t.start() 16 17 while threading.active_count() != 1: 18 pass #print threading.active_count() 19 else: 20 print('----all threads done---') 21 print(num)
线程间同步和交互:
通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
1 import threading 2 import random 3 import time 4 def light(): 5 if not event.isSet(): 6 event.set()#wait就不阻塞#绿灯状态 7 count=0 8 while True: 9 if count<10: 10 print('\033[42;1m--green light on---\033[0m') 11 elif count<13: 12 print('\033[43;1m--yellow light on---\033[0m') 13 elif count<20: 14 if event.isSet(): 15 event.clear() 16 print('\033[41;1m--green light on---\033[0m') 17 else: 18 count=0 19 event.set() 20 time.sleep(1) 21 count+=1 22 def car(n): 23 while 1: 24 time.sleep(1) 25 if event.isSet():#绿灯 26 print("car[%s] is running..."%n) 27 else: 28 print("car [%s] is waiting for the red light..."%n) 29 event.wait() 30 if __name__=='__main__': 31 event=threading.Event() 32 Light=threading.Thread(target=light) 33 Light.start() 34 for i in range(3): 35 t=threading.Thread(target=car,args=(i,)) 36 t.start()
多进程:
1 from multiprocessing import Process 2 import time 3 def f(name): 4 time.sleep(2) 5 print('hello', name) 6 7 if __name__ == '__main__': 8 p = Process(target=f, args=('bob',)) 9 p2 = Process(target=f, args=('bob',)) 10 p.start() 11 p2.start()#该句和上面同步执行 12 p.join()
注意:由于进程之间的数据需要各自持有一份,所以创建进程需要的非常大的开销。
下面是一个扩展的例子,可以显示每个进程所涉及的ID:
1 from multiprocessing import Process 2 import os 3 4 def info(title): 5 print(title) 6 print('module name:', __name__) 7 print('parent process:', os.getppid()) 8 print('process id:', os.getpid()) 9 print("\n\n") 10 11 def f(name): 12 info('\033[31;1mfunction f\033[0m') 13 print('hello', name) 14 15 if __name__ == '__main__': 16 info('\033[32;1mmain process line\033[0m') 17 p = Process(target=f, args=('bob',)) 18 p.start() 19 p.join()
进程间通讯
不同进程间内存是不共享的,要想实现两个进程间的数据交换,可以用以下方法:
Queues
使用方法跟threading里的queue差不多
1 from multiprocessing import Process,Queue 2 def f(q): 3 q.put([42,None,'hello']) 4 if __name__=='__main__': 5 que=Queue() 6 p=Process(target=f,args=(que,)) 7 p2=Process(target=f,args=(que,)) 8 p.start() 9 p2.start() 10 print('from parent:',que.get()) 11 print('from parent2:',que.get()) 12 p.join()
Pipes
1 from multiprocessing import Process, Pipe 2 3 def f(conn): 4 conn.send([42, None, 'hello']) 5 conn.close() 6 7 if __name__ == '__main__': 8 parent_conn, child_conn = Pipe() 9 p = Process(target=f, args=(child_conn,)) 10 p.start() 11 print(parent_conn.recv()) # prints "[42, None, 'hello']" 12 p.join()
Managers
1 from multiprocessing import Process, Manager 2 3 def f(d, l): 4 d[1] = '1' 5 d['2'] = 2 6 d[0.25] = None 7 l.append(1) 8 print(l) 9 10 if __name__ == '__main__': 11 with Manager() as manager: 12 d =manager.dict() 13 14 l = manager.list(range(5)) 15 p_list = [] 16 for i in range(10): 17 p = Process(target=f, args=(d, l)) 18 p.start() 19 p_list.append(p) 20 for res in p_list: 21 res.join() 22 23 print(d) 24 print(l)
进程同步:
1 from multiprocessing import Process, Lock 2 3 def f(l, i): 4 l.acquire() 5 try: 6 print('hello world', i) 7 finally: 8 l.release() 9 10 if __name__ == '__main__': 11 lock = Lock() 12 13 for num in range(10): 14 Process(target=f, args=(lock, num)).start()
进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:
1 from multiprocessing import Process,Pool 2 import time 3 4 def Foo(i): 5 time.sleep(2) 6 return i+100 7 8 def Bar(arg): 9 print('-->exec done:',arg) 10 11 pool = Pool(5) 12 13 for i in range(10): 14 pool.apply_async(func=Foo, args=(i,),callback=Bar) 15 #pool.apply(func=Foo, args=(i,)) 16 17 print('end') 18 pool.close() 19 pool.join()#进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。