亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么深入理解Python中的ThreadLocal變量

發布時間:2021-11-02 17:42:49 來源:億速云 閱讀:196 作者:柒染 欄目:web開發

怎么深入理解Python中的ThreadLocal變量,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

在上篇我們看到了 ThreadLocal 變量的簡單使用,中篇對python中 ThreadLocal  的實現進行了分析,但故事還沒有結束。本篇我們一起來看下Werkzeug中ThreadLocal的設計。

Werkzeug 作為一個 WSGI  工具庫,由于一些方面的考慮,并沒有直接使用python內置的ThreadLocal類,而是自己實現了一系列Local類。包括簡單的Local,以及在此基礎上實現的LocalStack,LocalManager  和 LocalProxy。接下來我們一起來看看這些類的使用方式,設計的初衷,以及具體的實現技巧。

Local 類的設計

Werkzeug 的設計者認為python自帶的ThreadLocal并不能滿足需求,主要因為下面兩個原因:

  • Werkzeug 主要用“ThreadLocal”來滿足并發的要求,python  自帶的ThreadLocal只能實現基于線程的并發。而python中還有其他許多并發方式,比如常見的協程(greenlet),因此需要實現一種能夠支持協程的Local對象。

  • WSGI不保證每次都會產生一個新的線程來處理請求,也就是說線程是可以復用的(可以維護一個線程池來處理請求)。這樣如果werkzeug  使用python自帶的ThreadLocal,一個“不干凈(存有之前處理過的請求的相關數據)”的線程會被用來處理新的請求。

為了解決這兩個問題,werkzeug  中實現了Local類。Local對象可以做到線程和協程之間數據的隔離,此外,還要支持清理某個線程或者協程下的數據(這樣就可以在處理一個請求之后,清理相應的數據,然后等待下一個請求的到來)。

具體怎么實現的呢,思想其實特別簡單,我們在深入理解Python中的ThreadLocal變量(上)  一文的***有提起過,就是創建一個全局字典,然后將線程(或者協程)標識符作為key,相應線程(或協程)的局部數據作為 value。這里 werkzeug  就是按照上面思路進行實現,不過利用了python的一些黑魔法,***提供給用戶一個清晰、簡單的接口。

具體實現

Local 類的實現在 werkzeug.local 中,以 8a84b62  版本的代碼進行分析。通過前兩篇對ThreadLocal的了解,我們已經知道了Local對象的特點和使用方法。所以這里不再給出Local對象的使用例子,我們直接看代碼。

class Local(object):     __slots__ = ('__storage__', '__ident_func__')          def __init__(self):         object.__setattr__(self, '__storage__', {})         object.__setattr__(self, '__ident_func__', get_ident)     ...

由于可能有大量的Local對象,為了節省Local對象占用的空間,這里使用 __slots__ 寫死了Local可以擁有的屬性:

  1. __storage__: 值為一個字典,用來保存實際的數據,初始化為空;

  2. __ident_func__:值為一個函數,用來找到當前線程或者協程的標志符。

由于Local對象實際的數據保存在__storage__中,所以對Local屬性的操作其實是對__storage__的操作。對于獲取屬性而言,這里用魔術方法__getattr__攔截__storage__  和  __ident_func__以外的屬性獲取,將其導向__storage__存儲的當前線程或協程的數據。而對于屬性值的set或者del,則分別用__setattr__和__setattr__來實現(這些魔術方法的介紹見屬性控制)。關鍵代碼如下所示:

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)

假設我們有ID為1,2, ... , N 的N個線程或者協程,每個都用Local對象保存有自己的一些局部數據,那么Local對象的內容如下圖所示:

怎么深入理解Python中的ThreadLocal變量

此外,Local類還提供了__release_local__方法,用來釋放當前線程或者協程保存的數據。

Local 擴展接口

Werkzeug 在 Local 的基礎上實現了 LocalStack 和 LocalManager,用來提供更加友好的接口支持。

LocalStack

LocalStack通過封裝Local從而實現了一個線程(或者協程)獨立的棧結構,注釋里面有具體的使用方法,一個簡單的使用例子如下:

ls = LocalStack() ls.push(12) print ls.top    # 12 print ls._local.__storage__ # {140735190843392: {'stack': [12]}}

LocalStack 的實現比較有意思,它將一個Local對象作為自己的屬性_local,然后定義接口push, pop 和 top  方法進行相應的棧操作。這里用 _local.__storage__._local.__ident_func__() 這個list來模擬棧結構。在接口push,  pop和top中,通過操作這個list來模擬棧的操作,需要注意的是在接口函數內部獲取這個list時,不用像上面黑體那么復雜,可以直接用_local的getattr()方法即可。以  push 函數為例,實現如下:

def push(self, obj):     """Pushes a new item to the stack"""     rv = getattr(self._local, 'stack', None)     if rv is None:         self._local.stack = rv = []     rv.append(obj)     return rv

pop 和 top 的實現和一般棧類似,都是對 stack = getattr(self._local, 'stack', None)  這個列表進行相應的操作。此外,LocalStack還允許我們自定義__ident_func__,這里用 內置函數 property  生成了描述器,封裝了__ident_func__的get和set操作,提供了一個屬性值__ident_func__作為接口,具體代碼如下:

def _get__ident_func__(self):     return self._local.__ident_func__  def _set__ident_func__(self, value):     object.__setattr__(self._local, '__ident_func__', value) __ident_func__ = property(_get__ident_func__, _set__ident_func__) del _get__ident_func__, _set__ident_func__

LocalManager

Local 和 LocalStack  都是線程或者協程獨立的單個對象,很多時候我們需要一個線程或者協程獨立的容器,來組織多個Local或者LocalStack對象(就像我們用一個list來組織多個int或者string類型一樣)。

Werkzeug實現了LocalManager,它通過一個list類型的屬性locals來存儲所管理的Local或者LocalStack對象,還提供cleanup方法來釋放所有的Local對象。Werkzeug中LocalManager最主要的接口就是裝飾器方法make_middleware,代碼如下:

def make_middleware(self, app):     """Wrap a WSGI application so that cleaning up happens after     request end.     """     def application(environ, start_response):         return ClosingIterator(app(environ, start_response), self.cleanup)     return application

這個裝飾器注冊了回調函數cleanup,當一個線程(或者協程)處理完請求之后,就會調用cleanup清理它所管理的Local或者LocalStack  對象(ClosingIterator 的實現在 werkzeug.wsgi中)。下面是一個使用 LocalManager 的簡單例子:

from werkzeug.local import Local, LocalManager  local = Local() local_2 = Local() local_manager = LocalManager([local, local2])  def application(environ, start_response):     local.request = request = Request(environ)     ...  # application 處理完畢后,會自動清理local_manager 的內容 application = local_manager.make_middleware(application)

通過LocalManager的make_middleware我們可以在某個線程(協程)處理完一個請求后,清空所有的Local或者LocalStack對象,這樣這個線程又可以處理另一個請求了。至此,文章開始時提到的第二個問題就可以解決了。Werkzeug.local  里面還實現了一個 LocalProxy 用來作為Local對象的代理,也非常值得去學習。

Python標準庫和Werkzeug在實現中都用到了很多python的黑魔法,不過最終提供給用戶的都是非常友好的接口。Werkzeug作為WSGI  工具集,為了解決Web開發中的特定使用問題,提供了一個改進版本,并且進行了一系列封裝,便于使用。不得不說,werkzeug的代碼可讀性非常好,注釋也是寫的非常棒,建議去閱讀源碼。

看完上述內容,你們掌握怎么深入理解Python中的ThreadLocal變量的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

泸定县| 深圳市| 藁城市| 台北市| 濉溪县| 苍南县| 巴塘县| 枣阳市| 望都县| 郁南县| 洞口县| 白沙| 峨眉山市| 阳高县| 来凤县| 容城县| 郯城县| 伊宁市| 安达市| 英山县| 镇坪县| 嘉义市| 乌拉特前旗| 六盘水市| 曲沃县| 吴江市| 奉贤区| 金山区| 炎陵县| 淅川县| 长宁区| 曲麻莱县| 玉门市| 宁陵县| 扶余县| 崇仁县| 辛集市| 绿春县| 突泉县| 丘北县| 怀远县|