首先环境为python3.6.8
Flask 版本 2.0.3
Flask-SQLAlchemy 版本 2.5.1
在网上查找Flask资料的时候,经常看到有flask默认是单线程的观点,我也一直信以为真,但是最近做的一个项目使用到了redis的互斥锁,大概业务逻辑是如果缓存中没有拿到数据,就会先去尝试拿到互斥锁,然后再进行缓存的重建,重建完后就会释放互斥锁,但是我在这个过程中发现,互斥锁被多次尝试拿取且拿取失败。
# 尝试加锁
def tryLock(key):
flag = Redis.setnx(key, 1)
print(flag,end=' ')
if flag:
Redis.expire(key, 10)
return True
return False
# 释放锁
def unLock(key):
return Redis.delete(key)
使用jmeter并发访问网站5s,访问2000次输出为
True False False False ....
但是问题是Flask不是单线程的吗,如果是单线程的话,锁资源只有被释放,上一个请求完成,我们的其他请求才能拿到锁资源
这样的话拿取锁资源一定会成功呀,为什么会失败呢?
所以,我开始尝试去查看Flask的源码,发现其实flask应该是从1.0版本之后就已经默认是多线程了
... versionchanged:: 1.0
If installed, python-dotenv will be used to load environment
variables from :file:`.env` and :file:`.flaskenv` files.
If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG`
environment variables will override :attr:`env` and
:attr:`debug`.
Threaded mode is enabled by default.
这是源码的注释,主要看最后一句
然后再往下看发现
options.setdefault("use_reloader", self.debug)
options.setdefault("use_debugger", self.debug)
options.setdefault("threaded", True)
最后一段代码则确定了我的猜想,确实默认是多线程。
后面看到了这段源码
try:
run_simple(t.cast(str, host), port, self, **options)
finally:
self._got_first_request = False
发现调用了run_simple,而run_simple其实是werkzeug启动的方法,其中是多线程默认是没有开启的。
因此得出结论,Flask的app.run方法封装了werkzeug的run_simple方法,在1.0版本之后就默认使用多线程了。
最后测试换成单线程后,果然就没有出现False了,且多线程的开启和DEBUG的开启也没有关系(看到有文章说有关系)。