内容:

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. 在U盘上安装Windows 7的详细步骤

    买到苹果新款MacBook Air后大家最想干的事是什么?体验一下Mac OS X?事实告诉我们有几乎一半的人第一件想要做的事是装一个微软的Windows系统,但问题是新版的MBA已经没有光驱了,这可 ...

  2. Slack 开发入门之 Incoming Webhooks:往 Slack 的 Channel 中发消息

    一个工程师团队使用 Slack 进行团队协作比 QQ / 微信流的效率高多啦.除了基本的 IM 之外,它的扩展性也是非常重要的一点. 本文介绍 Slack 的开发入门:Incoming Webhook ...

  3. Omar Loves Candies

    题目大意:在一个N * M的格子中,放有一些糖,这些糖有的会损害健康,有的对健康有益.有损害的被记为负数,有益的会记为正数.另外,对于每一个糖而言,他都比左边的糖和上面的糖更健康. 现在我要在在N*M ...

  4. CTF-练习平台-Misc之 图片又隐写

    八.图片又隐写 修改后缀名为zip,打开解压出两个文件 解压发现有密码,用WinHex打开发现文件头是zip的,所以出来把后缀名改为zip,再用工具爆破 最后得到解压密码,解压后是一个图片,改后缀名为 ...

  5. 《DSP using MATLAB》Problem 4.21

    快到龙抬头,居然下雪了,天空飘起了雪花,温度下降了近20°. 代码: %% -------------------------------------------------------------- ...

  6. 迭代器Iterator的底层实现原理

    第一步:没有接口的迭代器简单实现原理 package com.bjsxt.xiaofei; /** * 迭代器底层原理 * 方法: * hasNext() * next() * remove() * ...

  7. LG3898 [湖南集训]大新闻

    题意 题目描述 **记者弄了个大新闻,这个新闻是一个在 [0,n) 内等概率随机选择的整数,记其为 x.为了尽可能消除这个大新闻对公众造成的不良印象,我们需要在 [0,n)内找到某一个整数 y,使得 ...

  8. python 获取中文文件名的输出

    #coding:utf8 if __name__ == '__main__': srcfile = u"D:/测试路径/测试文件.txt" f = open(srcfile.enc ...

  9. 表单添加缩略图及截图js代码

    此为表单提交是上传截图的代码,待优化: // 添加小程序图片 function addAvatar(obj){ var file = obj.files[0]; limit($('.avatar_bo ...

  10. Git常用有用命令

    1.git reset --hard <commit_id>   //正真的退回到了<commit_id>状态,git记录个源码都退到那个状态了. 2.git reflog   ...