摘要:線程局部變量,也就是每個線程的私有變量,具有線程隔離性。按我們正常的理解,應該是每一個請求對應一個處理線程。在中,除了線程之外,還有個叫協程的東東,這里不提進程。配合會確保不管是協程還是線程,只要當前請求處理完成之后清除中對應的內容。
首先貼出官方文檔地址:http://werkzeug.pocoo.org/doc...
幾個local?
threading.local
werkzeug.local模塊中的:
Local
LocalStack
LocaProxy
why not threading.local?
threading.local,以前接觸過java的,對這個再熟悉不過了。線程局部變量,也就是每個線程的私有變量,具有線程隔離性。
按我們正常的理解,應該是每一個http請求對應一個處理線程。那么這樣看來使用threading.local應該夠了,為什么werkzeug還自己搞了一套?裝逼?非也。
在python中,除了線程之外,還有個叫協程的東東,(這里不提進程)。java中貌似是無法實現協程的。而python的協程感覺高大尚的樣子,python3.5開始對協程內置支持,而且也有相關開源庫greenlet等。
協程是什么?
舉個例子,比如一個線程在處理IO時,該線程是處于空閑狀態的,等待IO返回。但是此時如果不讓我們的線程干等著cpu時間片耗光,有沒有其他辦法,解決思路就是采用協程處理任務,一個線程中可以運行多個協程,當當前協程去處理IO時,線程可以馬上調度其他協程繼續運行,而不是干等著不干活。
這么一說,我們知道了協程會復用線程,WSGI不保證每個請求必須由一個線程來處理,如果WSGI服務器不是每個線程派發一個請求,而是每個協程派發一個請求,所以如果使用thread local變量可能會造成請求間數據相互干擾,因為一個線程中存在多個請求。
所以werkzeug給出了自己的解決方案:werkzeug.local模塊。
from werkzeug.local import Local, LocalManager local = Local() local_manager = LocalManager([local]) def application(environ, start_response): local.request = request = Request(environ) ... application = local_manager.make_middleware(application)
Local配合LocalManager會確保不管是協程還是線程,只要當前請求處理完成之后清除Local中對應的內容。
>>> loc = Local() >>> loc.foo = 42 >>> release_local(loc) >>> hasattr(loc, "foo")
當然,你也可以調用werkzeug.local.release_local(local)手動釋放Local或者LocalStack ,但是不能清除代理對象LocalProxy(代理對象底層保留了對Local對象的引用,以便在之后釋放)的數據。
>>> ls = LocalStack() >>> ls.push(42) >>> ls.top 42 >>> ls.push(23) >>> ls.top 23 >>> ls.pop() 23 >>> ls.top
LocalStack,與Local類似,但是管理數據的方式是采用棧的方式,可以通過LocalManager對象強制釋放,但是不建議這么做,而是通過其pop方法彈出。
from werkzeug.local import Local l = Local() # these are proxies request = l("request") user = l("user") from werkzeug.local import LocalStack _response_local = LocalStack() # this is a proxy response = _response_local()
werkzeug.local.LocalProxy:Local對象的一個代理。如果你需要創建Local或LocalStack對象的代理,可以直接call。
session = LocalProxy(lambda: get_current_request().session) from werkzeug.local import Local, LocalProxy local = Local() request = LocalProxy(local, "request") >>> from werkzeug.local import LocalProxy >>> isinstance(request, LocalProxy) True
你也可以通過LocalProxy構造一個代理對象,參數為可以調用的對象或者函數。
_get_current_object()返回被代理的對象。
werkzeug.local模塊關鍵部分代碼:
import copy from functools import update_wrapper from werkzeug.wsgi import ClosingIterator from werkzeug._compat import PY2, implements_bool try: from greenlet import getcurrent as get_ident except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident def release_local(local): local.__release_local__() class Local(object): __slots__ = ("__storage__", "__ident_func__") def __init__(self): object.__setattr__(self, "__storage__", {}) object.__setattr__(self, "__ident_func__", get_ident) def __iter__(self): return iter(self.__storage__.items()) def __call__(self, proxy): """Create a proxy for a name.""" return LocalProxy(self, proxy) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) class LocalStack(object): def __init__(self): self._local = Local() def __release_local__(self): self._local.__release_local__() def __call__(self): def _lookup(): rv = self.top if rv is None: raise RuntimeError("object unbound") return rv return LocalProxy(_lookup) def push(self, obj): rv = getattr(self._local, "stack", None) if rv is None: self._local.stack = rv = [] rv.append(obj) return rv def pop(self): stack = getattr(self._local, "stack", None) if stack is None: return None elif len(stack) == 1: release_local(self._local) return stack[-1] else: return stack.pop() @property def top(self): try: return self._local.stack[-1] except (AttributeError, IndexError): return None class LocalManager(object): def cleanup(self): for local in self.locals: release_local(local) def make_middleware(self, app): def application(environ, start_response): return ClosingIterator(app(environ, start_response), self.cleanup) return application @implements_bool class LocalProxy(object): def __init__(self, local, name=None): object.__setattr__(self, "_LocalProxy__local", local) object.__setattr__(self, "__name__", name) def _get_current_object(self): if not hasattr(self.__local, "__release_local__"): return self.__local() try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError("no object bound to %s" % self.__name__)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44323.html
摘要:但是這些對象和全局變量不同的是它們必須是動態的,因為在多線程或者多協程的情況下,每個線程或者協程獲取的都是自己獨特的對象,不會互相干擾。中有兩種上下文和。就是實現了類似的效果多線程或者多協程情況下全局變量的隔離效果。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應用啟動流程 flask 源碼解析:路由 flas...
摘要:主要的作用是將維護的字典中鍵為對應的值定義為。如果沒有,則會將當前到中,同時將加入列表中否則添加。注意清理之后的動作。上述代碼涉及到,它強調必須是一個可調用對象。后期的工作之一是了解。這僅僅是我的個人理解。實際上這是解決多個實例運行的問題。 Flask 中的上下文對象 知乎問題 編程中什么是「Context(上下文)」 已經能夠簡單地說明什么是 Context,它是一個程序需要的外部對...
摘要:并且棧頂的元素都是的請求上下文和應用上下文之后,我們再在這個環境中嵌套的應用上下文。這時查看兩個棧的內容,發現兩個棧中只有的請求的請求上下文對象和應用上下文對象。而等一直指向棧頂的請求上下文對象,分別引用請求上下文的和。 在Flask中處理請求時,應用會生成一個請求上下文對象。整個請求的處理過程,都會在這個上下文對象中進行。這保證了請求的處理過程不被干擾。處理請求的具體代碼如下: de...
摘要:的上下文對象有兩種上下文,分別是請求上下文請求的對象,封裝了請求的內容,生命周期請求處理完就結束了根據請求中的,重新載入該訪問者相關的會話信息應用上下文處理請求時用作臨時存儲的對象。 Werkzeugs 是 Flask 的底層WSGI庫。 什么是WSGI? showImg(https://s1.ax1x.com/2018/11/13/iOqdKS.jpg); 一段簡單的app: def...
摘要:具體怎么實現的呢,思想其實特別簡單,我們在深入理解中的變量上一文的最后有提起過,就是創建一個全局字典,然后將線程或者協程標識符作為,相應線程或協程的局部數據作為。 在上篇我們看到了 ThreadLocal 變量的簡單使用,中篇對python中 ThreadLocal 的實現進行了分析,但故事還沒有結束。本篇我們一起來看下Werkzeug中ThreadLocal的設計。 Werkzeug...
閱讀 1768·2021-10-11 10:57
閱讀 2352·2021-10-08 10:14
閱讀 3393·2019-08-29 17:26
閱讀 3340·2019-08-28 17:54
閱讀 3021·2019-08-26 13:38
閱讀 2885·2019-08-26 12:19
閱讀 3608·2019-08-23 18:05
閱讀 1277·2019-08-23 17:04