2016-12-03 202 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,而後臺線程只能啓動一次。