2016-12-03 6 views
0

私は、Brewコントローラをビジュアル化するはずのsocketioを使用して小さなFlask webappを作成しました。ハードウェアはRaspberry Piであり、コントローラー部分(ハードウェアのバインディングとデータ収集)は別のバックグラウンドスレッドで行われ、これはcreate_appで開始されます。バックグラウンドスレッドが(たとえ複数のアプリケーションオブジェクトを作成しても)一度だけ起動するようにする必要があります。だから私はBrewController.get_instance()関数を使用して何らかのシングルトンパターンを実現します。バックグラウンドスレッドはFlask-Socketioで2回起動する

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) 

しかし、スレッドはまだ2回も開始されており、2つの異なるBrewControllerインスタンスも取得します。だから私はデータベースの2倍のデータと重複した値で終わります。

出力私は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) 

しかし、このダブルスタートの最初の理由は何ですか?私にとっては、2つのプロセスが作成されたようです。誰かが何が起こっているのか、これを防ぐ最善の方法は何かを説明することはできますか?

答えて

1

リローダを使用すると、実際には2つのプロセスが作成されます。リローダは、変更のためにすべてのソースファイルを監視する唯一の目的でマスタプロセスを開始します。リローダプロセスは、実際のサーバを子プロセスとして実行し、ソースファイルの1つが変更されたことを検出すると、サーバを強制終了し、別のプロセスを開始します。

スレッドを起動するためのやや良い方法は、before_first_requestハンドラで行うことができます。そうすれば、子プロセスの実際のサーバだけが、最初のリクエストを取得したときにスレッドを開始します。リローダプロセスは決してリクエストを受信しないため、スレッドの起動を試みません。

+0

私はbefore_first_requestハンドラを使う考えが好きです。しかし、私はちょうどどこにそれを配置するか分からないので、私はまだ少し困っています。私はファクトリ関数 'create_app'を使うので、私はモジュール内で利用可能な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に基づいてbrew_controllerの作成を処理し、バックグラウンドスレッドを起動する関数create_appの中にbefore_first_requestハンドラを配置しました。

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 

ここではリローダを使用できますが、バックグラウンドスレッドは1度しか起動しません。

関連する問題