Flask 中的全局变量是线程安全的吗?如何在请求之间共享数据? [英] Are global variables thread-safe in Flask? How do I share data between requests?

查看:27
本文介绍了Flask 中的全局变量是线程安全的吗?如何在请求之间共享数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,通过发出请求来更改公共对象的状态,而响应取决于状态.

In my application, the state of a common object is changed by making requests, and the response depends on the state.

class SomeObj():
    def __init__(self, param):
        self.param = param
    def query(self):
        self.param += 1
        return self.param

global_obj = SomeObj(0)

@app.route('/')
def home():
    flash(global_obj.query())
    render_template('index.html')

如果我在我的开发服务器上运行它,我希望得到 1、2、3 等等.如果同时从 100 个不同的客户端发出请求,是否会出现问题?预期的结果是 100 个不同的客户端每个看到一个从 1 到 100 的唯一数字.或者会发生这样的事情:

If I run this on my development server, I expect to get 1, 2, 3 and so on. If requests are made from 100 different clients simultaneously, can something go wrong? The expected result would be that the 100 different clients each see a unique number from 1 to 100. Or will something like this happen:

  1. 客户端 1 查询.self.param 增加 1.
  2. 在 return 语句可以执行之前,线程切换到客户端 2.self.param 再次递增.
  3. 线程切换回客户端 1,例如,客户端返回数字 2.
  4. 现在线程移动到客户端 2 并向他/她返回数字 3.
  1. Client 1 queries. self.param is incremented by 1.
  2. Before the return statement can be executed, the thread switches over to client 2. self.param is incremented again.
  3. The thread switches back to client 1, and the client is returned the number 2, say.
  4. Now the thread moves to client 2 and returns him/her the number 3.

由于只有两个客户端,因此预期结果是 1 和 2,而不是 2 和 3.跳过了一个数字.

Since there were only two clients, the expected results were 1 and 2, not 2 and 3. A number was skipped.

当我扩展我的应用程序时,这真的会发生吗?我应该查看全局变量的哪些替代方案?

Will this actually happen as I scale up my application? What alternatives to a global variable should I look at?

推荐答案

您不能使用全局变量来保存此类数据.它不仅不是线程安全的,也不是进程安全的,而且生产环境中的 WSGI 服务器会产生多个进程.如果您使用线程来处理请求,您的计数不仅会出错,而且还会因处理请求的进程而异.

You can't use global variables to hold this sort of data. Not only is it not thread safe, it's not process safe, and WSGI servers in production spawn multiple processes. Not only would your counts be wrong if you were using threads to handle requests, they would also vary depending on which process handled the request.

使用 Flask 之外的数据源来保存全局数据.根据您的需要,数据库、memcached 或 redis 都是合适的独立存储区域.如果您需要加载和访问 Python 数据,请考虑 multiprocessing.Manager.您还可以将会话用于每个用户的简单数据.

Use a data source outside of Flask to hold global data. A database, memcached, or redis are all appropriate separate storage areas, depending on your needs. If you need to load and access Python data, consider multiprocessing.Manager. You could also use the session for simple data that is per-user.

开发服务器可以在单线程和进程中运行.您不会看到您描述的行为,因为每个请求都将被同步处理.启用线程或进程,您将看到它.app.run(threaded=True)app.run(processes=10).(在 1.0 中,服务器默认是线程化的.)

The development server may run in single thread and process. You won't see the behavior you describe since each request will be handled synchronously. Enable threads or processes and you will see it. app.run(threaded=True) or app.run(processes=10). (In 1.0 the server is threaded by default.)

某些 WSGI 服务器可能支持 gevent 或其他异步工作线程.全局变量仍然不是线程安全的,因为仍然没有针对大多数竞争条件的保护.你仍然可以有一个场景,一个工人得到一个值,产生,另一个修改它,产生,然后第一个工人也修改它.

Some WSGI servers may support gevent or another async worker. Global variables are still not thread safe because there's still no protection against most race conditions. You can still have a scenario where one worker gets a value, yields, another modifies it, yields, then the first worker also modifies it.

如果您需要在请求期间存储一些全局数据,您可以使用 Flask 的 g 对象.另一种常见情况是一些管理数据库连接的顶级对象.这种类型的全局"的区别在于它对每个请求都是唯一的,而不是 请求之间使用,并且有一些东西可以管理资源的设置和拆除.

If you need to store some global data during a request, you may use Flask's g object. Another common case is some top-level object that manages database connections. The distinction for this type of "global" is that it's unique to each request, not used between requests, and there's something managing the set up and teardown of the resource.

这篇关于Flask 中的全局变量是线程安全的吗?如何在请求之间共享数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆