網(wǎng)站服務(wù)器服務(wù)商3d建模培訓(xùn)班一般多少錢
一、local對(duì)象
背景:
多線成并發(fā)操作一個(gè)變量,會(huì)導(dǎo)致數(shù)據(jù)錯(cuò)亂,可以使用互斥鎖加鎖處理數(shù)據(jù)不安全的情況 (臨界區(qū))
解決:
使用local對(duì)象處理,多個(gè)線程操作的變量是local對(duì)象,就不會(huì)有并發(fā)安全的問(wèn)題。因?yàn)樗幚砹瞬l(fā)安全的問(wèn)題---->請(qǐng)求統(tǒng)一放在一個(gè)大字典中,key值是線程id號(hào),value是個(gè)字典。
# {111:{'name':jack},222:{'name':roma}}
l=local()
l.name='jack'---->l[111][name]
l.name='roma'---->l[222][name]
1.基本使用
不使用local,多線程并發(fā)操作,數(shù)據(jù)錯(cuò)亂
import time
from threading import Thread
class Local():passl = Local()def task(name):l.name = name time.sleep(1)print('在線程內(nèi)的名字是:', name, 'l對(duì)象中的名字大概率不一樣', l.name)if __name__ == '__main__':for i in range(10):t = Thread(target=task, args=['jack' + str(i) + '號(hào)', ])t.start() # 等待所有線程都執(zhí)行完成再執(zhí)行下面代碼time.sleep(6)print(l)
使用local
import time
from threading import Thread
from threading import local# 定義一個(gè)全局變量,并發(fā)安全的local,多個(gè)線程操作,不會(huì)錯(cuò)亂,因?yàn)槊總€(gè)線程用的都是自己的數(shù)據(jù)
l = local()def task(name):l.name = nametime.sleep(1)print('在線程內(nèi)的名字是:', name, 'l對(duì)象中的名字也是', l.name)if __name__ == '__main__':for i in range(10):t = Thread(target=task, args=['jack' + str(i) + '號(hào)', ])t.start()# 等待所有線程都執(zhí)行完成再執(zhí)行下面代碼time.sleep(6)print(l)
2.自己寫一個(gè)local類,線程和協(xié)程并發(fā)安全
通過(guò)字典自定義threading.local(函數(shù)):
from threading import get_ident,Thread
import time
storage = {}
def set(k,v):ident = get_ident() # 線程id號(hào)if ident in storage:storage[ident][k] = velse:storage[ident] = {k:v}
def get(k):ident = get_ident()return storage[ident][k]
def task(arg):set('val',arg) #v = get('val')print(v)# 10個(gè)線程跑完,最終storage={123:{val:0},222:{val:1},333:{val:2},444:{val:3}.....}
for i in range(10):t = Thread(target=task,args=(i,))t.start()
使用面向?qū)ο?/strong>:
from threading import get_ident,Thread
import time
class Local(object):storage = {}def set(self, k, v):ident = get_ident()if ident in Local.storage:Local.storage[ident][k] = velse:Local.storage[ident] = {k: v}def get(self, k):ident = get_ident()return Local.storage[ident][k]
obj = Local()
def task(arg):obj.set('val',arg)v = obj.get('val')print(v)
for i in range(10):t = Thread(target=task,args=(i,))t.start()
通過(guò)__setattr__和__getattr__方法實(shí)現(xiàn):
from threading import get_ident,Thread
import time
class Local(object):storage = {}def __setattr__(self, k, v):ident = get_ident()if ident in Local.storage:Local.storage[ident][k] = velse:Local.storage[ident] = {k: v}def __getattr__(self, k):ident = get_ident()return Local.storage[ident][k]
obj = Local()
def task(arg):obj.val = argprint(obj.val)
for i in range(10):t = Thread(target=task,args=(i,))t.start()
每個(gè)local對(duì)象用自己的存儲(chǔ)空間(字典):
from threading import get_ident, Thread
import timeclass Local(object):def __init__(self):# self.storage={} # 不能這樣寫 會(huì)遞歸object.__setattr__(self, 'storage', {})def __setattr__(self, k, v):ident = get_ident() # 獲取線程id號(hào)if ident in self.storage:self.storage[ident][k] = velse:self.storage[ident] = {k: v} #def __getattr__(self, k):ident = get_ident()return self.storage[ident][k]obj = Local()def task(name):obj.name = nameprint(obj.name)for i in range(10):t = Thread(target=task, args=(i,))t.start()
兼容線程和協(xié)程:
try:from greenlet import getcurrent as get_ident
except Exception as e:from threading import get_identfrom threading import Thread
import time
class Local(object):def __init__(self):object.__setattr__(self,'storage',{})def __setattr__(self, k, v):ident = get_ident()if ident in self.storage:self.storage[ident][k] = velse:self.storage[ident] = {k: v}def __getattr__(self, k):ident = get_ident()return self.storage[ident][k]
obj = Local()
def task(arg):obj.val = argobj.xxx = argprint(obj.val)
for i in range(10):t = Thread(target=task,args=(i,))t.start()
二、flask上下文源碼分析
請(qǐng)求上下文執(zhí)行流程(ctx):
-0 flask項(xiàng)目一啟動(dòng),有6個(gè)全局變量-_request_ctx_stack:LocalStack對(duì)象---->封裝了local-_app_ctx_stack :LocalStack對(duì)象-request : LocalProxy對(duì)象-session : LocalProxy對(duì)象-1 請(qǐng)求來(lái)了 app.__call__()---->內(nèi)部執(zhí)行:self.wsgi_app(environ, start_response)-2 wsgi_app()-2.1 執(zhí)行:ctx = self.request_context(environ):返回一個(gè)RequestContext對(duì)象,并且封裝了request(當(dāng)次請(qǐng)求的request對(duì)象),session-2.2 執(zhí)行: ctx.push():RequestContext對(duì)象的push方法-2.2.1 push方法中中間位置有:_request_ctx_stack.push(self),self是ctx對(duì)象-2.2.2 去_request_ctx_stack對(duì)象的類中找push方法(LocalStack中找push方法)-2.2.3 push方法源碼:def push(self, obj):#通過(guò)反射找self._local,在init實(shí)例化的時(shí)候生成的:self._local = Local()#Local()flask封裝的支持線程和協(xié)程的local對(duì)象# 一開(kāi)始取不到stack,返回Nonerv = getattr(self._local, "stack", None)if rv is None:#走到這,self._local.stack=[],rv=self._local.stackself._local.stack = rv = []# 把ctx放到了列表中#self._local={'線程id1':{'stack':[ctx,]},'線程id2':{'stack':[ctx,]},'線程id3':{'stack':[ctx,]}}rv.append(obj)return rv-3 如果在視圖函數(shù)中使用request對(duì)象,比如:print(request)-3.1 會(huì)調(diào)用request對(duì)象的__str__方法,request類是:LocalProxy-3.2 LocalProxy中的__str__方法:lambda x: str(x._get_current_object())-3.2.1 內(nèi)部執(zhí)行self._get_current_object()-3.2.2 _get_current_object()方法的源碼如下:def _get_current_object(self):if not hasattr(self.__local, "__release_local__"):#self.__local() 在init的時(shí)候,實(shí)例化的,在init中:object.__setattr__(self, "_LocalProxy__local", local)# 用了隱藏屬性#self.__local 實(shí)例化該類的時(shí)候傳入的local(偏函數(shù)的內(nèi)存地址:partial(_lookup_req_object, "request"))#加括號(hào)返回,就會(huì)執(zhí)行偏函數(shù),也就是執(zhí)行_lookup_req_object,不需要傳參數(shù)了#這個(gè)地方的返回值就是request對(duì)象(當(dāng)此請(qǐng)求的request,沒(méi)有亂)return self.__local()try:return getattr(self.__local, self.__name__)except AttributeError:raise RuntimeError("no object bound to %s" % self.__name__)-3.2.3 _lookup_req_object函數(shù)源碼如下:def _lookup_req_object(name):#name是'request'字符串#top方法是把第二步中放入的ctx取出來(lái),因?yàn)槎荚谝粋€(gè)線程內(nèi),當(dāng)前取到的就是當(dāng)次請(qǐng)求的ctx對(duì)象top = _request_ctx_stack.topif top is None:raise RuntimeError(_request_ctx_err_msg)#通過(guò)反射,去ctx中把request對(duì)象返回return getattr(top, name)-3.2.4 所以:print(request) 實(shí)質(zhì)上是在打印當(dāng)此請(qǐng)求的request對(duì)象的__str__-4 如果在視圖函數(shù)中使用request對(duì)象,比如:print(request.method):實(shí)質(zhì)上是取到當(dāng)次請(qǐng)求的reuquest對(duì)象的method屬性-5 最終,請(qǐng)求結(jié)束執(zhí)行: ctx.auto_pop(error),把ctx移除掉
其他的東西:
-session:-請(qǐng)求來(lái)了opensession-ctx.push()---->也就是RequestContext類的push方法的最后的地方:if self.session is None:#self是ctx,ctx中有個(gè)app就是flask對(duì)象, self.app.session_interface也就是它:SecureCookieSessionInterface()session_interface = self.app.session_interfaceself.session = session_interface.open_session(self.app, self.request)if self.session is None:#經(jīng)過(guò)上面還是None的話,生成了個(gè)空sessionself.session = session_interface.make_null_session(self.app)-請(qǐng)求走了savesession-response = self.full_dispatch_request() 方法內(nèi)部:執(zhí)行了before_first_request,before_request,視圖函數(shù),after_request,savesession-self.full_dispatch_request()---->執(zhí)行:self.finalize_request(rv)-----》self.process_response(response)----》最后:self.session_interface.save_session(self, ctx.session, response)-請(qǐng)求擴(kuò)展相關(guān)before_first_request,before_request,after_request依次執(zhí)行-信號(hào)的觸發(fā)信號(hào)名.send()-flask有一個(gè)請(qǐng)求上下文,一個(gè)應(yīng)用上下文-ctx:-是:RequestContext對(duì)象:封裝了request和session-調(diào)用了:_request_ctx_stack.push(self)就是把:ctx放到了那個(gè)位置-app_ctx:-是:AppContext(self) 對(duì)象:封裝了當(dāng)前的app和g-調(diào)用 _app_ctx_stack.push(self) 就是把:app_ctx放到了那個(gè)位置-g是個(gè)什么鬼?專門用來(lái)存儲(chǔ)用戶信息的g對(duì)象,g的全稱的為global g對(duì)象在一次請(qǐng)求中的所有的代碼的地方,都是可以使用的(當(dāng)次請(qǐng)求中傳遞一些數(shù)據(jù))-代理模式-request和session就是代理對(duì)象,用的就是代理模式-g對(duì)象和session的區(qū)別g對(duì)象只對(duì)當(dāng)次請(qǐng)求有效(當(dāng)此請(qǐng)求內(nèi)有效)session:可以跨請(qǐng)求,該用戶的多次請(qǐng)求中都可以使用
總結(jié):
1 flask中間件,使用請(qǐng)求擴(kuò)展,完成django中間件的功能
2 快速生成當(dāng)前項(xiàng)目的依賴(兩種方案)
- pipreqs
3 函數(shù)和方法的區(qū)別
- 面向?qū)ο笾杏蟹椒ǖ母拍?/li>
- 綁定給對(duì)象的,綁定給類的方法
- 特殊之處是自動(dòng)傳值
- 方法有可能是函數(shù)(對(duì)象的綁定方法,如果類來(lái)調(diào)用,就是函數(shù))
4 偏函數(shù) partial
- 提前給函數(shù)傳值
5 local對(duì)象
- 解決并發(fā)安全的問(wèn)題
- 多條線程操作同一個(gè)變量,會(huì)出現(xiàn)數(shù)據(jù)安全問(wèn)題,解決該問(wèn)題,需要加鎖
- 每條線程操作的都是自己線程的數(shù)據(jù)
- threading包下的local類---->實(shí)例化得到對(duì)象---->多線程并發(fā)操作---->數(shù)據(jù)不會(huì)錯(cuò)亂
6 自定義local對(duì)象
self.name='lqz' # 會(huì)觸發(fā)__setattr__---->會(huì)出現(xiàn)遞歸
setattr(self,'name','lqz') # 會(huì)出現(xiàn)遞歸
object.__setattr__(self,'name','lqz') # 不會(huì)出現(xiàn)遞歸
self.__dict__() # 屬性字典,也不會(huì)出現(xiàn)遞歸
7 flask請(qǐng)求上下文:RequestContext---->ctx:request,session,flash
8 flask應(yīng)用上下文:AppContext---->app_ctx:當(dāng)前app,g
9 ctx對(duì)象:請(qǐng)求上下文,flask整個(gè)請(qǐng)求的流程
請(qǐng)求來(lái)了---->app()---->觸發(fā)Flask類的__call__方法---->app.wsgi_app()ctx = self.request_context(environ) # ctx中包含當(dāng)此請(qǐng)求的request,session,把ctx放到了local對(duì)象中,來(lái)一個(gè)請(qǐng)求就放一次,local處理了并發(fā)安全,所以自己放的都是自己的,相互不影響response = self.full_dispatch_request()# 執(zhí)行請(qǐng)求擴(kuò)展,執(zhí)行視圖函數(shù)或者視圖類,處理了session,還有信號(hào)不管在整個(gè)過(guò)程中是否出異常,ctx都從local對(duì)象上移除
10 flask請(qǐng)求上下文源碼分析
1 請(qǐng)求來(lái)了執(zhí)行---->app()---->觸發(fā)類的__call__---->self.wsgi_app(environ, start_response)--->2 ctx = self.request_context(environ)---->封裝了request和session3 ctx.push()---->_request_ctx_stack.push(self)---->self是ctx4 _request_ctx_stack是LocalStack類的對(duì)象---->push
5 LocalStack的push方法源碼def push(self, obj):rv = getattr(self._local, "stack", None)if rv is None:self._local.stack = rv = []# self._local={'線程id1':{'stack':[ctx,]},'線程id2':{'stack':[ctx,]},'線程id3':{'stack':[ctx,]}}rv.append(obj)return rv6 LocalStack對(duì)像中的 _local--->init初始化出來(lái)的
self._local = Local() # 咱們自己寫的可以多線程并發(fā)訪問(wèn)的Local7 local={線程或協(xié)程id號(hào):{stack:[ctx]},線程或協(xié)程id號(hào):{stack:[ctx]}}
local.stack---->取stack的值,在不同協(xié)程下,取到的是自己的8 在視圖函數(shù)中使用request,session---->都是當(dāng)此請(qǐng)求的request和session,但是我們使用了全局變量。打印的真的是當(dāng)次請(qǐng)求的Request類的對(duì)象,但實(shí)際上request根本不是Request類的對(duì)象,LocalProxy類的對(duì)象,LocalProxy類重寫了__str__9 print(request)的時(shí)候---->類的__str__10 LocalProxy把所有的魔法方法都重寫了,因?yàn)樗莻€(gè)代理類11 request = LocalProxy(partial(_lookup_req_object, "request"))
init---->def __init__(self, local, name=None):---->object.__setattr__(self, "_LocalProxy__local", local)--->local就是偏函數(shù)12 LocalProxy---》__str__--->_get_current_object()是LocalProxy類的方法13 LocalProxy._get_current_object()---》return self.__local()--》加括號(hào)執(zhí)行偏函數(shù)---》partial(_lookup_req_object, "request")()--->_lookup_req_object('request')14 返回了當(dāng)前線程所在的ctx中的request對(duì)象15 request.method-->就是當(dāng)前線程的request對(duì)象的method方法16 在視圖函數(shù)中打印print(session)--->是當(dāng)此請(qǐng)求的session# 應(yīng)用上下文
# g到底是什么,是一個(gè)全局變量,放和取在當(dāng)次請(qǐng)求中的數(shù)據(jù)# session:open_session save_session# 信號(hào)的觸發(fā)位置
# 請(qǐng)求擴(kuò)展中三個(gè):的執(zhí)行位置