单例
- 单例在多线程下,可以保证全局唯一,但在多进程下呢?
- 子进程不共享父进程的变量
所以,每个进程都维护着一个自己的单例。
验证
创建appserver
以flask
为例,以包的形式创建一个单例
# view.py--------------------------------------------------------------
from single import indexer
from flask import Blueprint
from flask import jsonify
from flask import request
view_bp = Blueprint("view_bp", __name__)
@view_bp.route("/get")
def get_name():
print("lock", id(indexer.mu))
print("indexer", id(indexer))
return jsonify({"code": 1, "msg": indexer.get()})
@view_bp.route("/set")
def set_name():
print("lock", id(indexer.mu))
print("indexer", id(indexer))
return jsonify({"code": 1, "msg": indexer.set(request.args.get("name"))})
# single.py ----------------------------------------------------------------------
from multiprocessing import Lock
class __Indexer:
names = set()
def __init__(self):
self.mu = Lock()
def set(self, name):
self.names.add(name)
def get(self):
return self.names.pop()
indexer = __Indexer()
# main.py -----------------------------------------------------------------------
from flask import Flask
from view import view_bp
app = Flask(__name__)
app.register_blueprint(view_bp)
if __name__ == '__main__':
# 多进程启动
app.run("0.0.0.0", port=8000, processes=4)
用gunicorn
去替换flask
的werkzurg
(也可以用uwsgi
去替换)
# gunicorn.conf
bind = "0.0.0.0:5000"
# 4个worker
workers = 4
backlog = 2048
pidfile = "log/gunicorn.pid"
# accesslog = "log/access.log"
# errorlog = "log/debug.log"
timeout = 600
debug=False
capture_output = True
假设
如果indexer
全局唯一,那么names
全局唯一,只要一次set一次get,程序不会报错
实操
set-get一次后程序奔溃,并且可以看到打印出来的内存地址两次不相同
结论:开启的四个进程,每个进程中都有自己的indexer
,并不是唯一。
将set
替换为Manger().List()
from multiprocessing import Lock
from multiprocessing import Manager
class __Indexer:
# names = set()
names = Manager().list()
def __init__(self):
self.mu = Lock()
def set(self, name):
# self.names.add(name)
self.names.append(name)
def get(self):
return self.names.pop()
indexer = __Indexer()
再次验证,依旧会显示从empty
中取值
使用类方法实现单例(不考虑高并发)
from multiprocessing import Lock
from multiprocessing import Manager
class _Indexer:
names = set()
# names = Manager().list()
instance = None
@classmethod
def new(cls):
if cls.instance is None:
cls.instance = cls()
return cls.instance
else:
return cls.instance
def __init__(self):
self.mu = Lock()
def set(self, name):
self.names.add(name)
# self.names.append(name)
def get(self):
return self.names.pop()
# indexer = _Indexer()
indexer = _Indexer.new()
将indexer
移出去
# from single import indexer
from single import _Indexer
from flask import Blueprint
from flask import jsonify
from flask import request
view_bp = Blueprint("view_bp", __name__)
indexer = _Indexer.new()
依旧报错,相当于将name
随机放到了某个进程下的names
当其他进程去取时,自然会报错。
重新打开Manager()
结论
到目前为止,可以确定,每一个进程都有自己的indexer
加锁
普通锁
放到single.py
from multiprocessing import Lock
from multiprocessing import Manager
mu = Lock()
class _Indexer:
names = set()
# names = Manager().list()
instance = None
@classmethod
def new(cls):
print("global",mu)
with mu:
if cls.instance is None:
cls.instance = cls()
return cls.instance
else:
return cls.instance
def __init__(self):
self.mu = Lock()
def set(self, name):
self.names.add(name)
# self.names.append(name)
def get(self):
return self.names.pop()
# indexer = _Indexer()
# indexer = _Indexer.new()
刚启动,就显示创建了4个锁。。。。
放到main.py
from flask import Flask
from view import view_bp
from multiprocessing import Lock
app = Flask(__name__)
app.register_blueprint(view_bp)
if __name__ == '__main__':
mu = Lock()
app.run("0.0.0.0", port=8000, processes=4)
循环导入,崩溃
Manager().Lock()
from multiprocessing import Lock
from multiprocessing import Manager
# mu = Lock()
mu = Manager().Lock()
class _Indexer:
names = set()
# names = Manager().list()
instance = None
@classmethod
def new(cls):
print(mu)
with mu:
if cls.instance is None:
cls.instance = cls()
return cls.instance
else:
return cls.instance
def __init__(self):
self.mu = Lock()
def set(self, name):
self.names.add(name)
# self.names.append(name)
def get(self):
return self.names.pop()
# indexer = _Indexer()
# indexer = _Indexer.new()
也是创建了四个锁
依旧不是全局唯一
到最后,我自己也乱了,,,,
总之就是,httpserver进程间数据不共享,单例也不是单例,如果要创建单例,应该用fd
是否存在去创建,这样所有的进程都可以"看得到这个fd锁"
import os
def lock():
while True:
if os.path.exists("./mutex.lock"):
return False
else:
with open("./mutex.lock", "wb") as fd:
pass
return True
def unlock():
os.remove("./mutex.lock")