Flask:线程安全 + 偏函数 + flask上下文 + 群聊单聊(geventwebsocket)

线程安全

示例一:

  线程不安全的多线程小程序,多个线程之间会数据会发生混乱,不准确

import time
import threading
# from threading import local

class Foo(object):   # 继承object 是不安全的
    pass

foo=Foo()

def add(i):
    foo.name=i
    time.sleep(1)
    print(foo.name,i,threading.current_thread().ident)

for i in range(5):
    th = threading.Thread(target=add,args=(i,))
    th.start()

'''
结果:
4 2 8136 4 1 12632 4 0 2068 4 4 5488 4 3 13900 分析: 因为停止一秒钟,每个进程再执行add函数,但是foo.name中地址都是一样的, 通过循环,foo.name=i 已经在1秒内执行到最后i变为4了 因此打印的都是4,与i值不对应,造成了混乱,这样的线程是不安全的 '''

 

示例二:

  解决上线线程不安全的问题,用了local

import time
import threading
from threading import local

class Foo(local):     # 这里继承local即可,不需要做其他的改动
    pass

foo=Foo()

def add(i):
    foo.name=i
    time.sleep(1)
    print(foo.name,i,threading.current_thread().ident)

for i in range(5):
    th = threading.Thread(target=add,args=(i,))
    th.start()

'''
为了解决线程安全问题,创建的类中继承local就可以了
将上面的代码做如上的更改:
结果会一一对应:
0 0 4820
1 1 3412
3 3 11448
2 2 8372
4 4 15400
'''

偏函数

 是将函数先partial到偏函数先不执行,等执行的时候再调用,在看flask的上下文源码的时候会看到

from functools import partial

def func(a,*args):
    print(a,args)
    return a

par_ab = partial(func,1)   # partial(函数名,参数a),得到的par_ab就是偏函数,func此时不会执行,1 作为参数传进 func
print(par_ab)        # 打印偏函数,得到的是偏函数的地址
print(par_ab(2))     # 2 传入的参数对应的是args;此时 func函数会执行,打印参数a args; 本身该句话要打印偏函数的返回值 a=1

# functools.partial(<function func at 0x0000025EDC5D9F28>, 1)

# 1 (2,)
# 1

flask上下文

上下文主要是看源码,分析app.run()内部是如何执行的

上文:主要是如何获取到request的

下文:主要是request.method 属性是如何调用的

这里需要继续看源码:

 

1、werkzeug中的run_simple()

app.run()简单原理解析


from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return 'ok'

if __name__ == '__main__':
    app.run(debug=True)


'''
flask的运行机制,app.run()
实际上就是封装了,run_simple
'''

# 下面是一个简单的 run_simple 启动flask
# 也是app.run()的原理

from werkzeug.serving import run_simple
from werkzeug.wrappers import Response,Request

@Request.application
def app(request):
    print(request.path)
    return Response("200 OK!")

run_simple("0.0.0.0",5000,app)

# 当有请求来的时候,会调用app,app():对象加括号会调用 __call__()方法

 

2、flask上下文

 

   flask上下文详解(lili)

 

单聊群聊(geventwebsocket)

后端 01.py

import json

from flask import Flask, render_template, request
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
from geventwebsocket.websocket import WebSocket

app = Flask(__name__)


@app.route("/")
def index():
    return render_template('talk.html')


user_socket_dict = {}

@app.route('/ws/<username>')
def ws(username):
    user_socket = request.environ.get('wsgi.websocket')  # 每次有登录访问,就会替换掉上次的内容websocket
    if user_socket:
        user_socket_dict[username] = user_socket   # 有人访问就将该用户的websocket添加到字典中,存储所有登录人的socket
    print("user_socket_dict",user_socket_dict)

    while 1:
        # -----------------  单聊  -----------------------------
        msg = user_socket.receive()      # 在这里会夯住,等待接收 前端 send()的信息
        print(msg)
        msg_dict = json.loads(msg)
        msg_dict['from_user'] = username
        to_user = msg_dict.get('to_user')          # 获取到收件人的username
        u_socket = user_socket_dict.get(to_user)    #  获取收件人的websocket信息
u_socket.send(json.dumps(msg_dict)) # 给收件人发送消息 #--------------------- 群聊 ----------------------------- # msg = user_socket.receive() # for u_socket in user_socket_dict.values(): # 群聊,去values即可,给聊表中的每一个websocket都发送收到的消息 # u_socket.send(msg) if __name__ == '__main__': # app.run(debug=True) # 利用WSGIServer来开启服务,代替之前的app.run http_serv = WSGIServer(('0.0.0.0', 5001), app, handler_class=WebSocketHandler) http_serv.serve_forever() ''' user_socket_dict = {'alex': <geventwebsocket.websocket.WebSocket object at 0x0000020C636D62B8>, 'chen': <geventwebsocket.websocket.WebSocket object at 0x0000020C636D6250>} '''

 

前端 talk.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h1>欢迎来到聊天界面</h1>

<!--  如果这里使用form标签包起来的话,里面的button一定要指明是type='button'  -->
<input type="text" id="username">
<button onclick="login()">登录聊天室</button><input type="text" id="to_user">发送:<input type="text" id="msg">
<button onclick="send_msg()">发送</button>
<div id="chat_list" style="background: lightgoldenrodyellow;width: 500px;height: 500px;"></div>


</body>

<script type="application/javascript">

    var ws = null;

    function login() {
        var username = document.getElementById("username").value;
        
        <!-- 生成ws websocket 访问后端, onmessage 是后端send返回的数据 -->
        ws = new WebSocket('ws://192.168.100.1:5001/ws/' + username);
        ws.onmessage = function (data) {
            console.log(data.data);
            var recv_msg = JSON.parse(data.data);
            var ptag = document.createElement('P');
            ptag.innerText = recv_msg.from_user + ':' + recv_msg.msg;
            document.getElementById('chat_list').appendChild(ptag);
        }
    }

    function send_msg() {
        var to_user = document.getElementById('to_user').value;
        var msg = document.getElementById('msg').value;
        var send_str = {
            "to_user": to_user,
            "msg": msg
        };
        <!-- 这里是给后端发送数据 -->
        ws.send(JSON.stringify(send_str));
    }

</script>
</html>

 

posted @ 2019-01-09 17:59  葡萄想柠檬  Views(382)  Comments(0)    收藏  举报
目录代码