内容:

1.进程线程复习

2.flask多线程的问题

3.线程隔离

4.LocalStack

5.flask上下文整理

6.多app应用

1.进程线程复习

(1)进程

进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元

简单说进程就是操作系统调度分配资源的单位,每一个应用程序至少有一个进程

(2)线程

线程可以说是进程的一部分,可以有一个线程也可以有多个线程

只用进程管理CPU资源,粒度太大了,需要更小的单元来管理,就出现了线程

(3)进程与线程的区别

  • 进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位
  • 线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程
  • 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间
  • 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的
  • 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮
  • 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
  • 执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
  • 线程是处理器调度的基本单位,但是进程不是

(4)多线程的好处

  • 多线程可以更加充分的利用CPU的性能优势
  • 实现异步编程

(5)其他

对于普通python(GIL)、node.js来说一般都是单进程单线程的

  • 对于CPU密集型程序,多线程无优势
  • 对于IO密集型程序,多线程有优势

2.flask多线程的问题

(1)flask与webserver

flask自带的run方法其实是自己的一个服务器,只是供我们测试调试用的,真正上线后我们要用webserver去运行flask而不是用flask自带的运行方法

另外flask自带的是单进程单线程的,不能同时处理多个请求,用webserver可以有效提高性能

常见的webserver:nginx、apache、IIS等

(2)flask多线程的问题

flask中可以开启多线程,开启方法:run方法中有一个threaded参数,设置为True开启多线程

开启之后可能会导致这个问题:

flask中通过线程隔离来解决这个问题

3.线程隔离

(1)flask线程隔离基本原理

现有全局变量request=None

  • 在线程1中 request=Request()
  • 在线程2中 request=Request()
  • 在线程3中 request=Request()

想取线程1的request, 现在的情况下肯定无法做到

解决办法:全局变量改为dict, 每一个线程都有对应的自己的key, 并将request作为value存放。falsk的线程隔离技术也是基于类似的原理实现的。

(2)源码

siet-package/werkzeug/local.py 中的Local类:

 class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
object.__setattr__(self, '__storage__', {}) # __storge__ 实现空的字典
object.__setattr__(self, '__ident_func__', get_ident) # get_ident是获取当前线程id号的 函数 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__() # 取当前线程的 线程id号,
storage = self.__storage__ # 类本身的字典
try:
storage[ident][name] = value # 把当前线程id号存起来
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)

Local类用当前线程的id号当作key存储不同的线程,不同线程操作该对象时互不影响,Local类就是线程隔离的对象

Local隔离实例:

 from werkzeug.local import Local
import threading, time my_obj = Local()
my_obj.b = 1 def worker():
my_obj.b = 2
print('in sub thread b is:' + str(my_obj.b)) # subthread1 = threading.Thread(target=worker, name='subthread1')
subthread1.start() time.sleep(1) print('my_obj.b is : {}'.format(my_obj.b)) #

(3)线程隔离的意义

使用线程隔离使当前线程能正确引用到自己创建的对象,而不是引用到其他线程所创建的对象

4.LocalStack

(1)源码

 class LocalStack(object):
def __init__(self):
self._local = Local() def __release_local__(self):
self._local.__release_local__() 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__ 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):
"""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 def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
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):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None

(2)LocalStack和Local关系

  • Local使用字典实现线程隔离
  • LocalStack封装了Local,实现了线程隔离的栈结构

(3)使用LocalStack

 from werkzeug.local import LocalStack       # 栈 先进后出
# LocalStack常用方法: push pop top s = LocalStack()
s.push(1)
print(s.top) # top只会取出元素不会删除
print(s.top)
print(s.pop()) # pop取出元素并删除
print(s.top) s.push(1)
s.push(2)
print(s.top)
print(s.top)
print(s.pop())
print(s.top)
 from werkzeug.local import LocalStack       # 栈 先进后出
from threading import Thread
import time my_stack = LocalStack()
my_stack.push(1)
print("in main thread after push, value is: " + str(my_stack.top)) # def worker():
# 新线程
print("in new thread before push, value is: " + str(my_stack.top)) # None
my_stack.push(2)
print("in new thread after push, value is: " + str(my_stack.top)) # new_t = Thread(target=worker, name="new thread")
new_t.start()
time.sleep(1) # 主线程
print("finally, in main thread value is: " + str(my_stack.top)) #

5.flask上下文整理

(1)请求上下文

 from flask import Flask,request,session,g,current_app

 app = Flask(__name__)

 @app.route('/',methods=['GET',"POST"])
def index():
# request是 LocalProxy 的对象
print(request) # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.request
request.method # LocalProxy.__getattr__ -->
# str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.request
# getattr(self._get_current_object(), name) --> ctx.request.method request.path # ctx.request.path print(session) # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.session print(g) # 执行g对象的__str__
return "index" if __name__ == '__main__':
# 1. app.__call__
# 2. app.wsgi_app
app.wsgi_app
app.request_class
app.run()

(2)应用上下文

 from flask import Flask,request,session

 app = Flask(__name__)

 @app.route('/',methods=['GET',"POST"])
def index():
# request是 LocalProxy 的对象
print(request) # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.request
request.method # LocalProxy.__getattr__ -->
# str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.request
# getattr(self._get_current_object(), name) --> ctx.request.method request.path # ctx.request.path print(session) # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 调用偏函数 --> ctx.session
return "index" if __name__ == '__main__':
# 1. app.__call__
# 2. app.wsgi_app
app.wsgi_app
app.run()
 from flask import Flask,request,g

 app = Flask(__name__)

 @app.before_request
def before():
g.permission_code_list = ['list','add'] @app.route('/',methods=['GET',"POST"])
def index():
print(g.permission_code_list)
return "index" if __name__ == '__main__':
app.run()

(3)请求上下文及应用上下文

 a. 请求上下文:
- request
- session b. 应用上下文:
请求流程:
_request_ctx_stack.local = { } _app_ctx_stack.local = { } 1. 请求到来 ,有人来访问
# 将请求相关的数据environ封装到了RequestContext对象中
# 再讲对象封装到local中(每个线程/每个协程独立空间存储)
# ctx.app # 当前APP的名称
# ctx.request # Request对象(封装请求相关东西)
# ctx.session # 空
_request_ctx_stack.local = {
唯一标识:{
"stack":[ctx, ]
},
唯一标识:{
"stack":[ctx, ]
},
} # app_ctx = AppContext对象
# app_ctx.app
# app_ctx.g _app_ctx_stack.local = {
唯一标识:{
"stack":[app_ctx, ]
},
唯一标识:{
"stack":[app_ctx, ]
},
} 2. 使用
from flask import request,session,g,current_app print(request,session,g,current_app) 都会执行相应LocalProxy对象的 __str__ current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session')) current_app = LocalProxy(_find_app)
g = LocalProxy(partial(_lookup_app_object, 'g')) 3. 终止,全部pop 问题1:多线程是如何体现?
问题2:flask的local中保存数据时,使用列表创建出来的栈。为什么用栈?
- 如果写web程序,web运行环境;栈中永远保存1条数据(可以不用栈)。
- 写脚本获取app信息时,可能存在app上下文嵌套关系。
from flask import Flask,current_app,globals,_app_ctx_stack app1 = Flask('app01')
app1.debug = False # 用户/密码/邮箱
# app_ctx = AppContext(self):
# app_ctx.app
# app_ctx.g app2 = Flask('app02')
app2.debug = True # 用户/密码/邮箱
# app_ctx = AppContext(self):
# app_ctx.app
# app_ctx.g with app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
# {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438>]}}
print(_app_ctx_stack._local.__storage__)
print(current_app.config['DEBUG']) with app2.app_context():
# {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438> ]}}
print(_app_ctx_stack._local.__storage__)
print(current_app.config['DEBUG']) print(current_app.config['DEBUG'])

6.多app应用

 from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
from flask import Flask, current_app app1 = Flask('app01')
app2 = Flask('app02') @app1.route('/index')
def index():
return "app01" @app2.route('/index2')
def index2():
return "app2" # http://www.oldboyedu.com/index --> app1
# http://www.oldboyedu.com/admin/index2 --> app2
app= DispatcherMiddleware(app1, {
'/admin': app2,
}) if __name__ == "__main__":
run_simple('localhost', 5000, app)

flask高阶的更多相关文章

  1. python 常用的高阶函数

    前言 高阶函数指的是能接收函数作为参数的函数或类:python中有一些内置的高阶函数,在某些场合使用可以提高代码的效率. map() map函数可以把一个迭代对象转换成另一个可迭代对象,不过在pyth ...

  2. python基础之常用的高阶函数

    前言 高阶函数指的是能接收函数作为参数的函数或类:python中有一些内置的高阶函数,在某些场合使用可以提高代码的效率. map() map函数可以把一个迭代对象转换成另一个可迭代对象,不过在pyth ...

  3. Python高阶用法总结

    目录 1. lambda匿名函数 1.1 函数式编程 1.2 应用在闭包 2. 列表解析式 3. enumerate内建函数 4. 迭代器与生成器 4.1 迭代器 4.3 生成器 5. 装饰器 前言: ...

  4. 08 . Python3高阶函数之迭代器、装饰器

    Python3高阶函数之迭代器.装饰器 列表生成式 推导式就是构建比较有规律的列表,生成器. 孩子,我现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],我要求你把列表里 ...

  5. c#语言-高阶函数

    介绍 如果说函数是程序中的基本模块,代码段,那高阶函数就是函数的高阶(级)版本,其基本定义如下: 函数自身接受一个或多个函数作为输入. 函数自身能输出一个函数,即函数生产函数. 满足其中一个条件就可以 ...

  6. swift 的高阶函数的使用代码

    //: Playground - noun: a place where people can play import UIKit var str = "Hello, playground& ...

  7. JavaScript高阶函数

    所谓高阶函数(higher-order function) 就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数. 下面的例子接收两个函数f()和g(),并返回一个新的函数用以计算f(g ...

  8. 分享录制的正则表达式入门、高阶以及使用 .NET 实现网络爬虫视频教程

    我发布的「正则表达式入门以及高阶教程」,欢迎学习. 课程简介 正则表达式是软件开发必须掌握的一门语言,掌握后才能很好地理解到它的威力: 课程采用概念和实验操作 4/6 分隔,帮助大家理解概念后再使用大 ...

  9. python--函数式编程 (高阶函数(map , reduce ,filter,sorted),匿名函数(lambda))

    1.1函数式编程 面向过程编程:我们通过把大段代码拆成函数,通过一层一层的函数,可以把复杂的任务分解成简单的任务,这种一步一步的分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基本单元. ...

随机推荐

  1. 【error】select timeout问题

    使用摄像头的过程中出现这个问题,说明是找不到摄像头了, 有可能是摄像头驱动问题,也有可能是摄像头接口处接触不良等原因造成的. re 1.select-timeout-opencv; End

  2. HDU 1003:Max Sum(DP,连续子段和)

    Max Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  3. HDU 3068 最长回文 manacher 算法,基本上是O(n)复杂度

    下面有别人的比较详细的解题报告: http://wenku.baidu.com/view/3031d2d3360cba1aa811da42.html 下面贴我的代码,注释在代码中: #include ...

  4. Word所有字体按比例缩小

    ctrl + [ 不然每次都要一部分一部分的修改啊

  5. 转 MetaWeblog API 编写

    如今,许多人都熟悉个人和公司或业界主办的博客.后者明显成为了传统公司和行业网站的下一代新兴产物.博客的内容涉及从简洁的特制产品公告和公共关系到实用且深刻的主题探索,这些主题可能对公司的产品或行业的未来 ...

  6. smarty学习——基本概念

    学习一种框架,我们最基本的就是掌握框架的思想,同时了解框架的基本语法. 1.对于定界符的了解 有的smarty模板标签都被加上了定界符. 默认情况下是 { 和},但它们是可被改变的.例如,我们假定你在 ...

  7. ser2net的编译及测试

    1. 将ser2net编译进内核 1.1 make menuconfig 1.2 选上ser2net NetWork——>ser2net 2. 烧写固件 3.ser2net配置文件: 修改/et ...

  8. GCC参数详解 一

    gcc是gnu compiler collection 的简称,他包含了多种语言的编译器,如C, C++, Objective-C, Objective-C++, Java, Fortran和Ada. ...

  9. 发布程序时出现“类型ASP.global_asax同时存在于...”错误的解决办法

    web程序发布后,通过浏览器访问程序显示如下的错误信息: 编译器错误消息: CS0433: 类型“ASP.global_asax”同时存在于“c:\WINDOWS\Microsoft.NET\Fram ...

  10. CentOS下Apache默认安装路径

    apache:如果采用RPM包安装,安装路径应在 /etc/httpd目录下apache配置文件:/etc/httpd/conf/httpd.confApache模块路径:/usr/sbin/apac ...