2017-02-19 184 views
1

全局字典我有以下的應用程序,運行一個調度程序,以週期性地更新全局變量的狀態(字典):更新從多個線程

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) 

在5秒的時間間隔工作導入的功能被在同一目錄中的不同文件中:

from app import inventory 

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

但是,這並不像我希望的那樣工作。導入的函數會更新庫存,但是更改絕不會傳播到原始字典,因此bump_inventory正在處理inventory的副本,或者它從不在函數作用域之外進行更新。在兩個不同的終端:

]$ 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-有沒有必要使用asyncio apscheduler。您擁有內置於asyncio中的所有設施,並且與Sanic一起運作良好。

2-不建議使用全局狀態,尤其是在Web應用程序場景中。您應該使用數據庫或Redis。但是,如果您出於某種原因需要應用程序狀態,則可以將其存儲在app對象上。

Sanic的下一個版本將有一個add_task方法,用於嚮應用程序添加asyncio任務。如果你想現在使用這個,你可以從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數據庫也是一個選項,但是清單中的數據非常簡單(對於簡單的線程安全字典而言是完美的),所以它看起來像是過度殺傷。在'app'中保持全局狀態而不是一個單獨的變量的優點是什麼? – mart1n

+1

另外,任何包含'add_task'的版本在PyPI中都可用? – mart1n