2012-04-04 52 views
2

我试图把握与NDB介绍异步操作,我想用@ndb.tasklet到异步我的一些工作。如何重写一个异步NDB方法,写自己的tasklet

的简单的例子就是在重写get_or_insert_async

被string_id代这是一个正确的方式来的东西呢?这里可以改进什么?

@classmethod 
@ndb.tasklet 
def get_or_insert_async(cls, *args): 
    id = cls.make_string_id(*args) 
    model = yield super(MyModel, cls).get_or_insert_async(id) 
    raise ndb.Return(model) 

另一个例子会在扇形的循环中做某事。它是否正确?

@classmethod 
@ndb.tasklet 
def do_stuff(cls, some_collection): 

    @ndb.tasklet 
    def internal_tasklet(data): 
     do_some_long_taking_stuff(data) 
     id = make_stuff_needed_for_id(data) 
     model = yield cls.get_or_insert_async(id) 
     model.long_processing(data) 
     yield model.put_async() 
     raise ndb.Return(None) 

    for data in some_collection: 
     # will it parallelise internal_tasklet execution? 
     yield internal_tasklet(data) 

    raise ndb.Return(None) 

编辑:

如所理解的整个概念,yields在这里提供一个Future对象其然后在平行(如果可能)收集并执行异步。我对么?

尼克的提示后,(是你的意思?):

@classmethod 
@ndb.tasklet 
def do_stuff(cls, some_collection): 

    @ndb.tasklet 
    def internal_tasklet(data): 
     do_some_long_taking_stuff(data) 
     id = make_stuff_needed_for_id(data) 
     model = yield cls.get_or_insert_async(id) 
     model.long_processing(data) 
     raise ndb.Return(model)    # change here 

    models = [] 
    for data in some_collection: 
     # will it parallelise internal_tasklet execution? 
     m = yield internal_tasklet(data)  # change here 
     models.appedn(m)      # change here 

    keys = yield ndb.put_multi_async(models) # change here 
    raise ndb.Return(keys)      # change here 

编辑:

新修订版本...

@classmethod 
@ndb.tasklet 
def do_stuff(cls, some_collection): 

    @ndb.tasklet 
    def internal_tasklet(data): 
     do_some_long_taking_stuff(data) 
     id = make_stuff_needed_for_id(data) 
     model = yield cls.get_or_insert_async(id) 
     model.long_processing(data) 
     raise ndb.Return(model)     

    futures = [] 
    for data in some_collection: 
     # tasklets won't run in parallel but while 
     # one is waiting on a yield (and RPC underneath) 
     # the other will advance it's execution 
     # up to a next yield or return 
     fut = internal_tasklet(data))   # change here 
     futures.append(fut)     # change here 

    Future.wait_all(futures)     # change here 

    models = [fut.get_result() for fut in futures] 
    keys = yield ndb.put_multi_async(models) # change here 
    raise ndb.Return(keys)      # change here 

回答

1

你并不需要使用微进程,如果所有你想要做的就是调用异步的东西用不同的参数 - 只返回被包装的函数的返回值,就像这样:

def get_or_insert_async(cls, *args): 
    id = cls.make_string_id(*args) 
    return super(MyModel, cls).get_or_insert_async(id) 

我是持谨慎态度的原因很多,但:你改变的意义内置的功能,这通常是一个坏主意,你改变了签名(位置参数,但没有关键字参数) ,而且您不会将其他参数传递给原始函数。

对于你的第二个例子中,产生的东西一次一个将迫使NDB等待他们完成 - “成品率”与“等待”的代名词。相反,对集合中的每个元素执行tasklet函数,然后同时等待所有元素(通过调用列表中的yield)。

+0

不可能将其在'get_or_insert_async'同步,只有底层调用原来'get_or_insert_async'真的异步'make_string_id'电话吗? – 2012-04-05 08:03:44

+0

你可以在你的答案中重写第二个例子吗?我不确定女巫的产量是否下降,因此循环不会等待每个元素和所有(或大部分)内部并行处理的'internal_tasklet'执行。 – 2012-04-05 08:07:37

+0

@WooYek是的,但make_string_id在你的代码片段中是同步的。如果他们没有制作RPC,那么异步执行它们就毫无意义 - 只有当时间用于RPC时,tasklet才有用。关于第二个例子 - 问题是在循环中调用yield的反模式。这样做等待每个单独的任务完成后再进入下一个任务。任何时候你有一个带有yield的循环,你应该调用没有yield的函数,汇总一个结果列表,并在该列表中调用yield。 – 2012-04-05 08:46:40