2017-02-19 14 views
1

からグローバル辞書を更新私は定期的にグローバル変数(辞書)の状態を更新するスケジューラを実行する次のアプリケーションを持っている:5秒間隔ジョブにインポート複数のスレッド

from sanic import Sanic 
from sanic.response import text 
from apscheduler.schedulers.background import BackgroundScheduler 
import bumper 

app = Sanic() 
scheduler = BackgroundScheduler() 

inventory = {1: 1, 2: 2} 

@scheduler.scheduled_job('interval', seconds=5) 
def bump(): 
    bumper.bump() 


@scheduler.scheduled_job('interval', seconds=10) 
def manual_bump(): 
    global inventory 
    inventory[2] += 1 


@app.route("/") 
async def test(request): 
    return text(inventory) 

if __name__ == "__main__": 

    scheduler.start() 
    app.run(host="0.0.0.0", port=8000) 

関数であります同じディレクトリ内の別のファイルに:

from app import inventory 

def bump_inventory(): 
    inventory[1] += 1 
    print('new', inventory) 

これは、しかし、私はそれが希望望んでいたように動作しません。インポートされた関数はインベントリを更新しますが、変更は元のディクショナリに伝播されることはありません。bump_inventoryinventoryのコピーを処理しているか、関数スコープの外部に更新されません。 2つの異なる端末で:

]$ python app.py 
2017-02-19 14:11:45,643: INFO: Goin' Fast @ http://0.0.0.0:8000 
2017-02-19 14:11:45,644: INFO: Starting worker [26053] 
new {1: 2, 2: 2} 
new {1: 3, 2: 2} 

]$ while true; do curl http://0.0.0.0:8000/; echo; sleep 1; done 
{1: 1, 2: 2} 
... 
{1: 1, 2: 3} 
... 

これを行う正しい方法は何ですか?

答えて

1

まだ共有変数が更新されていない理由はわかりません(私の推測はまだコピーです)が、引数として関数に渡すのはうまくいきます(実際のオブジェクトではなくオブジェクトに参照を渡しているので)。これに5秒の間隔を変更する動作:

@scheduler.scheduled_job('interval', seconds=5) 
def bump(): 
    global inventory 
    bumper.bump(inventory) 

これは、他のファイル(すなわちfrom app import inventoryを除去)周期的インポートを除去します。

3

1- apschedulerをasyncioと併用する必要はありません。あなたはasyncioに組み込まれて必要なすべての設備を持っており、それはSanicとうまく連動します。

2-グローバルステートの使用は、特にWebアプリケーションのシナリオではお勧めできません。データベースまたはRedisを使用する必要があります。しかし何らかの理由でアプリケーション状態が必要な場合は、appオブジェクトに直接格納することができます。

Sanicの次のリリースには、アプリケーションにasyncioタスクを追加するためのadd_taskメソッドがあります。これを今使用したい場合はGithubからマスターブランチをインストールできます:

import asyncio 
from sanic import Sanic 
from sanic.response import text 

app = Sanic() 
app.inventory = {1:1, 2:2} 


async def five_second_job(app): 
    while True: 
     app.inventory[1] += 1 
     await asyncio.sleep(5) 


async def ten_second_job(app): 
    while True: 
     app.inventory[2] += 2 
     await asyncio.sleep(10) 


@app.route("/") 
async def test(request): 
    return text(app.inventory) 

if __name__ == "__main__": 
    app.add_task(five_second_job(app)) 
    app.add_task(ten_second_job(app)) 
    app.run(host="0.0.0.0", port=9000) 
+1

ありがとう!私はまだasyncioのものにかなり新しいので、これは非常に有用な情報です。私がグローバルな状態を作りたい理由は、データベースを気にせず、すべてをメモリに保持することです。インメモリのSQLite DBもオプションですが、インベントリ内のデータは非常にシンプル(スレッドセーフな単純なdictには完璧です)なので、過剰な使用に見えます。別の変数としてではなく、 'app'でグローバル状態を保持する利点は何ですか? – mart1n

+1

また、 'add_task'を含むリリースがPyPIで利用できるようになった時はいつでも推測できますか? – mart1n

関連する問題