成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Werkzeug Local與LocalProxy等淺析

The question / 1725人閱讀

摘要:線程局部變量,也就是每個線程的私有變量,具有線程隔離性。按我們正常的理解,應(yīng)該是每一個請求對應(yīng)一個處理線程。在中,除了線程之外,還有個叫協(xié)程的東東,這里不提進程。配合會確保不管是協(xié)程還是線程,只要當(dāng)前請求處理完成之后清除中對應(yīng)的內(nèi)容。

首先貼出官方文檔地址:http://werkzeug.pocoo.org/doc...
幾個local?
threading.local
werkzeug.local模塊中的:
Local
LocalStack
LocaProxy

why not threading.local?

threading.local,以前接觸過java的,對這個再熟悉不過了。線程局部變量,也就是每個線程的私有變量,具有線程隔離性。

按我們正常的理解,應(yīng)該是每一個http請求對應(yīng)一個處理線程。那么這樣看來使用threading.local應(yīng)該夠了,為什么werkzeug還自己搞了一套?裝逼?非也。

在python中,除了線程之外,還有個叫協(xié)程的東東,(這里不提進程)。java中貌似是無法實現(xiàn)協(xié)程的。而python的協(xié)程感覺高大尚的樣子,python3.5開始對協(xié)程內(nèi)置支持,而且也有相關(guān)開源庫greenlet等。

協(xié)程是什么?
舉個例子,比如一個線程在處理IO時,該線程是處于空閑狀態(tài)的,等待IO返回。但是此時如果不讓我們的線程干等著cpu時間片耗光,有沒有其他辦法,解決思路就是采用協(xié)程處理任務(wù),一個線程中可以運行多個協(xié)程,當(dāng)當(dāng)前協(xié)程去處理IO時,線程可以馬上調(diào)度其他協(xié)程繼續(xù)運行,而不是干等著不干活。

這么一說,我們知道了協(xié)程會復(fù)用線程,WSGI不保證每個請求必須由一個線程來處理,如果WSGI服務(wù)器不是每個線程派發(fā)一個請求,而是每個協(xié)程派發(fā)一個請求,所以如果使用thread local變量可能會造成請求間數(shù)據(jù)相互干擾,因為一個線程中存在多個請求。
所以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會確保不管是協(xié)程還是線程,只要當(dāng)前請求處理完成之后清除Local中對應(yīng)的內(nèi)容。

>>> loc = Local()
>>> loc.foo = 42
>>> release_local(loc)
>>> hasattr(loc, "foo")

當(dāng)然,你也可以調(diào)用werkzeug.local.release_local(local)手動釋放Local或者LocalStack ,但是不能清除代理對象LocalProxy(代理對象底層保留了對Local對象的引用,以便在之后釋放)的數(shù)據(jù)。

>>> ls = LocalStack()
>>> ls.push(42)
>>> ls.top
42
>>> ls.push(23)
>>> ls.top
23
>>> ls.pop()
23
>>> ls.top

LocalStack,與Local類似,但是管理數(shù)據(jù)的方式是采用棧的方式,可以通過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對象的一個代理。如果你需要創(chuàng)建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構(gòu)造一個代理對象,參數(shù)為可以調(diào)用的對象或者函數(shù)。
_get_current_object()返回被代理的對象。

werkzeug.local模塊關(guān)鍵部分代碼:

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__)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/44323.html

相關(guān)文章

  • flask 源碼解析:上下文

    摘要:但是這些對象和全局變量不同的是它們必須是動態(tài)的,因為在多線程或者多協(xié)程的情況下,每個線程或者協(xié)程獲取的都是自己獨特的對象,不會互相干擾。中有兩種上下文和。就是實現(xiàn)了類似的效果多線程或者多協(xié)程情況下全局變量的隔離效果。 這是 flask 源碼解析系列文章的其中一篇,本系列所有文章列表: flask 源碼解析:簡介 flask 源碼解析:應(yīng)用啟動流程 flask 源碼解析:路由 flas...

    Labradors 評論0 收藏0
  • 深入 Flask 源碼理解 Context

    摘要:主要的作用是將維護的字典中鍵為對應(yīng)的值定義為。如果沒有,則會將當(dāng)前到中,同時將加入列表中否則添加。注意清理之后的動作。上述代碼涉及到,它強調(diào)必須是一個可調(diào)用對象。后期的工作之一是了解。這僅僅是我的個人理解。實際上這是解決多個實例運行的問題。 Flask 中的上下文對象 知乎問題 編程中什么是「Context(上下文)」 已經(jīng)能夠簡單地說明什么是 Context,它是一個程序需要的外部對...

    ninefive 評論0 收藏0
  • Flask中的請求上下文和應(yīng)用上下文

    摘要:并且棧頂?shù)脑囟际堑恼埱笊舷挛暮蛻?yīng)用上下文之后,我們再在這個環(huán)境中嵌套的應(yīng)用上下文。這時查看兩個棧的內(nèi)容,發(fā)現(xiàn)兩個棧中只有的請求的請求上下文對象和應(yīng)用上下文對象。而等一直指向棧頂?shù)恼埱笊舷挛膶ο螅謩e引用請求上下文的和。 在Flask中處理請求時,應(yīng)用會生成一個請求上下文對象。整個請求的處理過程,都會在這個上下文對象中進行。這保證了請求的處理過程不被干擾。處理請求的具體代碼如下: de...

    sourcenode 評論0 收藏0
  • flask 核心 之 應(yīng)用上下文 及 請求上下文

    摘要:的上下文對象有兩種上下文,分別是請求上下文請求的對象,封裝了請求的內(nèi)容,生命周期請求處理完就結(jié)束了根據(jù)請求中的,重新載入該訪問者相關(guān)的會話信息應(yīng)用上下文處理請求時用作臨時存儲的對象。 Werkzeugs 是 Flask 的底層WSGI庫。 什么是WSGI? showImg(https://s1.ax1x.com/2018/11/13/iOqdKS.jpg); 一段簡單的app: def...

    tinna 評論0 收藏0
  • 深入理解Python中的ThreadLocal變量(下)

    摘要:具體怎么實現(xiàn)的呢,思想其實特別簡單,我們在深入理解中的變量上一文的最后有提起過,就是創(chuàng)建一個全局字典,然后將線程或者協(xié)程標(biāo)識符作為,相應(yīng)線程或協(xié)程的局部數(shù)據(jù)作為。 在上篇我們看到了 ThreadLocal 變量的簡單使用,中篇對python中 ThreadLocal 的實現(xiàn)進行了分析,但故事還沒有結(jié)束。本篇我們一起來看下Werkzeug中ThreadLocal的設(shè)計。 Werkzeug...

    dadong 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<