如何使用线程本地数据python学习

#如何使用线程本地数据
#实际案例:实现一个web视频监控服务器,服务器端采集摄像头数据,客户端使用浏览器
#通过http请求接收数据,服务器使用推送方式(multipart/x-mixed-replace)
# 一直使用一个tcp连接向客户端传递数据,这种方式将持续占用一个线程,导致单线程服务器
# 无法处理多客户端请求
#改写程序,在每个线程中处理一个客户端请求,支持多客户端访问。
#解决方案:threading.local函数可以创建线程本地数据空间,其下属性对每个线程独立存在
"""
import threading
l=threading.local()
l.x=1  #x主线程本地的数据  其他地方访问不到
def f():
  print l.x
f()
threading.Thread(target=f).statr()#启动子线程中会抛出异常找不到属性

"""
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)
        
        
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.serve_forever()

 

你可能感兴趣的:(求职笔试+面试)