2012-04-04 4 views
2

Я пытаюсь понять асинхронные операции, введенные с помощью NDB, я хотел бы использовать @ndb.tasklet для async некоторых из моих работ.Как переопределить метод асинхронного NDB и написать свой собственный тасклет

Простой пример будет string_id поколения в перегруженной get_or_insert_async

Является ли это правильный путь к вещам? Что можно улучшить здесь?

@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 

EDIT:

Новый пересмотренный вариант ...

@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 ждать по их завершению - «yield» является синонимом «wait». Вместо этого выполните функцию тасклета для каждого элемента в коллекции, а затем дождитесь их всех (вызывая выход из списка) одновременно.

+0

Не может ли он сделать вызов 'make_string_id' в' get_or_insert_async' синхронным и только базовый вызов исходного 'get_or_insert_async' действительно асинхронным? –

+0

Можете ли вы переписать второй пример в своем ответе? Я не уверен, что ведьма уйдет, поэтому цикл не будет ждать каждого элемента и всех (или большинства) 'inner_tasklet' исполнений, где они будут распараллеливаться. –

+0

@WooYek Да, но make_string_id в любом случае синхронно в вашем фрагменте. И если они не делают RPC, нет смысла делать их асинхронно - таллеты помогают, когда время тратится на RPC. Что касается второго примера - проблема заключается в анти-шаблоне вызова в цикле. Выполнение этого ждет завершения каждой отдельной задачи, прежде чем перейти к следующему. Каждый раз, когда у вас есть цикл с доходностью в нем, вы должны вызывать функцию без урока, собирать список результатов и вызывать доход в этом списке. –

Смежные вопросы