Werkzeug 和 class 与 Flask 的状态:当 class 没有被重新初始化时,class 成员变量如何重置?

Werkzeug and class state with Flask: How are class member variables resetting when the class isn't being reinitialized?

我正在尝试编写一个烧瓶扩展,需要在请求之间保留一些信息。当我 运行 Werkzeug 使用单个进程时,这工作正常,但是当我 运行 使用多个进程时,我得到一些我不理解的奇怪行为。以这个简单的应用为例:

from flask import Flask
app = Flask(__name__)

class Counter(object):
    def __init__(self, app):
        print('initializing a Counter object')
        self.app = app
        self.value = 0

    def increment(self):
        self.value += 1
        print('Just incremented, current value is ', self.value)

counter = Counter(app)

@app.route('/')
def index():
    for i in range(4):
        counter.increment()
    return 'index'

if __name__ == '__main__':
    #scenario 1 - single process
    #app.run()
    #scenario 2 - threaded
    #app.run(threaded=True)
    #scenario 3 - two processes
    app.run(processes=2)

对于前两个场景,它的行为完全符合我的预期:Counter 对象被初始化一次,然后随着对“/”路由的每个请求而递增。当我 运行 它与第三种情况(传递进程=2)然后我得到这个作为输出:

 initializing a Counter object
  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 Just incremented, current value is  1
 Just incremented, current value is  2
 Just incremented, current value is  3
 Just incremented, current value is  4
 127.0.0.1 - - [30/Aug/2015 09:47:25] "GET / HTTP/1.1" 200 -
 Just incremented, current value is  1
 Just incremented, current value is  2
 Just incremented, current value is  3
 Just incremented, current value is  4
 127.0.0.1 - - [30/Aug/2015 09:47:26] "GET / HTTP/1.1" 200 -
 Just incremented, current value is  1
 Just incremented, current value is  2
 Just incremented, current value is  3
 Just incremented, current value is  4
 127.0.0.1 - - [30/Aug/2015 09:47:27] "GET / HTTP/1.1" 200 -

似乎 counter.value 在初始化后立即返回到它的状态,而实际上并未重新初始化。有人可以阐明 Werkzeug 在内部为实现这一目标所做的工作吗?我也很想知道是否有办法让这个行为像我天真地期望的那样(两个进程,每个进程都有自己的 Counter 实例)。谢谢!

第一个示例(单线程)只使用了Counter,所以它有效。

第二个例子(多线程),生成线程来处理每个请求。它们与在它们生成之前创建的 Counter 共享内存,因此每次递增它们都会产生相同的结果。

最后一个例子(多进程),产生进程来处理每个请求。 Flask's dev server uses fork: each child sees the same starting point (counter is already initialized) but increments in their own address space 请求结束时消失。

import os

class Counter:
    def __init__(self):
        print('init')
        self.value = 0

    def increment(self):
        self.value += 1
        print('inc -> {}'.format(self.value))

counter = Counter()

def multi():
    if not os.fork():
        # child starts with copy of parent memory
        for _ in range(3):
            # increments three times
            counter.increment()

        # child is done
        os._exit(0)

# three processes run
for _ in range(3):
    multi()
init
inc -> 1
inc -> 2
inc -> 3
inc -> 1
inc -> 2
inc -> 3
inc -> 1
inc -> 2
inc -> 3

使用数据库或其他外部存储跨进程存储全局状态,使用 before_after_request。请注意,这并不完全简单,因为您必须以线程安全的方式存储每个请求的计数器增量值,这样两个线程就不会同时覆盖该值。

req 1 starts, gets stored value = 4
req 2 starts, gets stored value = 4
req 1 increments, value = 8
req 1 saves, value = 8
req 2 increments, value = 8
req 2 saves, value = 8 but should = 12