2016-12-03 52 views
0

我已经使用socketio创建了一个小的Flask webapp,它应该可视化一个brew控制器。硬件是Raspberry Pi,控制器部分(硬件绑定和数据收集)是在独立的后台线程中完成的,该线程在create_app开始。我需要确保后台线程只开始一次(即使我创建了多个应用程序对象)。所以我使用BrewController.get_instance()函数来实现某种单例模式。后台线程用Flask-Socketio启动两次

import os 
import time 
import threading 
import arrow 
from sqlitedict import SqliteDict 
from flask import Flask 
from flask_bootstrap import Bootstrap 
from flask_socketio import SocketIO 
from flaskext.lesscss import lesscss 

from config import config 
from .brewcontroller import BrewController 

background_thread = threading.Thread() 

# Flask Plugins 
bootstrap = Bootstrap() 
socketio = SocketIO() 
brew_controller = BrewController.get_instance() 


db = SqliteDict('process_data.sqlite', tablename='pd', autocommit=False) 
db.setdefault('t', []) 
db.setdefault('temp_sp', []) 
db.setdefault('temp_ct', []) 
db.setdefault('ht_pwr', []) 
db.commit() 

from . import events # noqa 


def create_app(config_name=None): 
    app = Flask(__name__) 

    if config_name is None: 
     config_name = os.environ.get('PIBREW_CONFIG', 'development') 
    app.config.from_object(config[config_name]) 

    # init flask plugins 
    lesscss(app) 
    bootstrap.init_app(app) 
    socketio.init_app(app) 

    # create blueprints 
    from .main import main as main_blueprint 
    app.register_blueprint(main_blueprint, url_prefix='/') 

    # init the brew controller and start the background task if none 
    # exists yet 
    print(brew_controller) 
    if not brew_controller.initialized: 
     brew_controller.init_app(app) 

     background_thread = threading.Thread(
      target=process_controller, 
      args=[app.config['PROCESS_INTERVAL']], 
      daemon=True 
     ) 
     print('controller started') 
     background_thread.start() 

    return app 


def process_controller(interval): 

    while(1): 

     current_time = arrow.now() 
     brew_controller.process() 

     data = { 
      't': current_time.format('HH:mm:ss'), 
      'temp_sp': '{:.1f}'.format(brew_controller.temp_setpoint), 
      'temp_ct': '{:.1f}'.format(brew_controller.temp_current), 
      'ht_en': brew_controller.heater_enabled, 
      'mx_en': brew_controller.mixer_enabled, 
      'ht_pwr': '{:.1f}'.format(brew_controller.heater_power_pct), 
      'ht_on': brew_controller.heater_on, 
     } 

     x = db['t'] 
     x.append(data['t']) 
     db['t'] = x 

     db['temp_sp'].append(data['temp_sp']) 
     db['temp_sp'] = db['temp_sp'] 

     db['temp_ct'].append(data['temp_ct']) 
     db['temp_ct'] = db['temp_ct'] 

     db['ht_pwr'].append(data['ht_pwr']) 
     db['ht_pwr'] = db['ht_pwr'] 

     db.commit() 

     socketio.emit('update', data) 
     time.sleep(interval) 

但是线程仍然开始两次,我甚至得到两个不同的BrewController实例。所以我最终得到了两倍于我的数据库和重复值的数据。

后,我打电话manage.py运行看起来像这样(我打印的brewcontroller实例,看看它们是不同的)输出:

<pibrew.brewcontroller.BrewController object at 0x105777208> 
<pibrew.brewcontroller.BrewController object at 0x105777208> 
controller started 
* Restarting with stat 
<pibrew.brewcontroller.BrewController object at 0x10ca04240> 
<pibrew.brewcontroller.BrewController object at 0x10ca04240> 
controller started 
* Debugger is active! 
* Debugger pin code: 121-481-821 
(31213) wsgi starting up on http://0.0.0.0:5000 

我发现通过设置use_reloader说法,我可以抑制这个我manage.py为False。

@manager.command 
def run(): 
    app = create_app() 
    socketio.run(app, host='0.0.0.0', port=5000, use_reloader=False) 

但是,首先这种双重启动的原因是什么。对我来说,似乎有两个创建的进程。有人可以解释发生了什么,以及防止这种情况的最佳方法是什么。

回答

1

当你使用重载器时,实际上有两个创建的进程。重新加载器启动一个主进程,唯一的目的是观察所有源文件的变化。重新加载进程将实际的服务器作为子进程运行,当它发现其中一个源文件被修改时,它会杀死服务器并启动另一个源文件。

一个稍微好一点的开始你的线程可能是做一个before_first_request处理方法。这样,只有子进程中的实际服务器在获得第一个请求时才会启动线程。重新加载进程永远不会收到请求,所以它永远不会尝试启动一个线程。

+0

我喜欢使用'before_first_request'处理程序的想法。但是我仍然有点麻烦,因为我不知道该把它放在哪里。当我使用工厂函数'create_app'时,模块中没有可用的应用程序对象。我也无法注册蓝图。 – MrLeeh

+0

您可以在蓝图上使用[before_app_first_request](http://flask.pocoo.org/docs/0.11/api/#flask.Blueprint.before_app_first_request)。 – Miguel

0

基于Miguels Answer我放在before_first_request处理我create_app函数处理brew_controller创建并启动后台线程里面。

def create_app(config_name=None): 
    app = Flask(__name__) 

    # ... 

    @app.before_first_request 
    def init_brew_controller(): 
     # init the brew controller and start the background task if none 
     # exists yet 
     if not brew_controller.initialized: 
      brew_controller.init_app(app) 

      background_thread = threading.Thread(
       target=process_controller, 
       args=[app.config['PROCESS_INTERVAL']], 
       daemon=True 
      ) 
      background_thread.start() 
      app.logger.info('started background thread') 

    return app 

现在我可以使用reloader,而后台线程只能启动一次。