#如何使用线程池
#实际案例:多线程web视频监控服务器,我们需要对请求连接数据做限制,
# 以防止恶意用户发起大量连接而导致服务器创建大量线程,最终因资源耗尽而瘫痪,
# 可以使用线程池替代原来每次请求创建的线程。
#解决方案:Python3中有线程池的实现
# 使用标准库中concurrent.futures下的ThreadPoolExecutor,
#对象的submit和map方法可以用来启动线程池中线程执行任务
"""
如何使用ThreadPoolExecutor
from concurrent.future import ThreadPoolExecutor
import time
executor =ThreadPoolExecutor(3)#创建对象
def f(a,b):#定义任务函数
print('f',a,b)
time.sleep(10)
return a**b
future=executor.submit(f,2,4)#代用线程池线程执行函数
furture.result()
executor.map(f,[2,3,4,6,7],[4,5,6,7,8])
"""
import os,cv2,time,struct,threading
from BaseHTPSever import HTTPSever,BaseHTTPRequestHandler
from SocketSever import TCPServer,ThreadingTCPServer
from threading import Thread,RLock
from select import select
#JpegStreamer负责采集数据 独立线程
class JpegStreamer(Thread):
def __init__(self,camera):
Thread.__init__(self)
self.cap =cv2.VideoCapture(camera)
self.lock=RLock()
self.pipes={}
#数据输出采用了一个注册的接口 如果想获取数据就调用这个函数
def register(self):
pr,pw = os.pipe()#在内部 创建管道
self.lock.acquire()
self.pipes[pr] =pw#维护管道的写端 来了数据从写端把数据写入进去
self.lock.release()
return pr#并且把管道的读端返回给用户
def unregister(self,pr):
self.lock.acquire()
self.pipes.pop(pr)
self.lock.release()
#数据采集 返回一个生成器对象
def capture(self):
cap=self.cap
while cap.isOpend():
ret,frame=cap.read()#在其内部使用opencvde 库
if ret:
#ret,data=cv2.imencode('.jpg',frame)
ret, data = cv2.imencode('.jpg', frame,(cv2.IMWRITE_JPEG_QUALITY,40))#并且编码成jpg图片
yield data.tostring()
#把其中的一帧发送到所有已注册的的管道当中去
def send(self,frame):
n=struct.pack('l',len(frame))
self.lock.acquire()
if len(self.pipes):
_,pipes,_=select([],self.pipes.itervalues(),[],1)
for pip in pipes:
os.write(pipe,n)
self.lock.release()
def run(self):
for frame in self.capture():#从capture()拿到数据
self.send(frame)#发送给所有的管道
class JpegRetriever(object):
def __init__(self,streamer):
self.streamer=streamer
#把每一个线程使用的pipe实现成线程本地数据 创建local
self.local=threading.local()
# self.pipe=streamer.register()
def retrieve(self):
while True:
ns=os.read(self.local.pipe,8)
n=struct.unpack('l',ns)[0]
data=os.read(self.local.pipe,n)
yield data
#使用上下文管理器
def __enter__(self):
if hasattr(self.local,'pipe'):
raise RuntimeError()
self.local.pipe = streamer.register()
return self.retrieve()
#def cleanup(self):
def __exit__(self, *args):
self.streamer.unregister(self.local.pipe)
#使用完之后删除相关属性
del self.local.pipe
return True#压制所有异常
class Handler(BaseHTTPRequestHandler):
retriever =None#只有一个对象
@staticmethod
def setJpegRetriever(retriever):
Handler.retriever=retriever
def do_GET(self):
if self.retriever is None:
raise RuntimeError('no retriever')
if self.path!='/':
return
self.send_response(200)#关于jpG响应头部的构造
self.send_header('Cintent-type','multipart/x-mixed-replace;boundary =abcde')
self.end_headers()
#使用时候使用上下文管理
with self.retriever as frames:
#for frame in self.retriever.retrieve():
for frame in frames:
self.send_frame(frame)
def send_frame(self,frame):
self.wfile.write('--abcde\r\n')
self.wfile.write('Content.Type:image/jpeg\r\n')
self.wfile.write('Content.Length:image/jpeg\r\n' % len(frame))
self.wfile.write(frame)
from concurrent.futures import ThreadPoolExecutor
#线程池 改进class ThreadingMixIn类中的process_request方法重定义它内部的处理线程的代码
class ThreadingPoolTCPSever(ThreadingTCPServer):
#复写方法 首先创建构造器 关注父类构造器TCPSever的参数复制过来即可
# 然后加一个参数指定线程数量
def __init__(self,sever_address,RequestHandlerClass,\
build_and_activate=True,max_threading_num=100):
super().__init__(sever_address,RequestHandlerClass,\
build_and_activate) #调用父类构造器
#创建线程池
self.executor=ThreadPoolExecutor(max_threading_num)
def process_request(self,request,client_adress):
self.executor().submit(self.process_request_thread,request,client_adress)
# t=threading.Thread(target=self.process_request_thread,args=(request,client_adress))
# t.daemon =self.daemon_threads
# t.start()
if __name__=='__main__':
streamer =JpegStreamer(0)
streamer.start()
retriever =JpegRetriever(streamer)
Handler.setJpegRetriever(retriever)
print('Start sever....')
#httpd=TCPServer(('',9000),Handler)
#httpd = ThreadingTCPServer(('', 9000), Handler)#会创建独立的线程
#替换成线程池解决
httpd = ThreadingPoolTCPServer(('', 9000), Handler,max_threading_num=3)#设置线程数为3
httpd.serve_forever()