2017-02-19 182 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