2009-11-26 7 views
10

Próbuję przekonwertować bibliotekę synchroniczną, aby użyć wewnętrznej asynchronicznej struktury IO. I kilka sposobów, które wyglądają następująco:Asynchroniczne wywołania zwrotne i generatory języka Python

def foo: 
    .... 
    sync_call_1() # synchronous blocking call 
    .... 
    sync_call_2() # synchronous blocking call 
    .... 
    return bar 

Dla każdej synchronicznych funkcji (sync_call_*) NapisaĹ,em odpowiedniej funkcji asynchronicznej, która przyjmuje wywołania zwrotnego. Na przykład.

def async_call_1(callback=none): 
    # do the I/O 
    callback() 

Teraz na pytanie pyton początkującego - Co najprostszy sposób na przełożenie istniejących metod zamiast używać tych nowych metod async? Oznacza to, że metoda foo() powyżej musi być teraz:

def async_foo(callback): 
    # Do the foo() stuff using async_call_* 
    callback() 

Oczywistym wyborem jest przeniesienie wywołania zwrotnego do każdej metody asynchronicznego, które skutecznie „powraca” do wywoływania funkcji „foo”, a następnie wywołać zwrotnego globalny w koniec metody. Jednak powoduje to, że kod jest kruchy, brzydki i musiałbym dodać nowe wywołanie zwrotne dla każdego połączenia z metodą async_call_*.

Czy istnieje prosty sposób, aby to zrobić za pomocą idiom Pythona, takich jak generator lub coroutine?

Odpowiedz

2

Istnieje kilka sposobów na zadania multipleksowania. Nie możemy powiedzieć, co jest najlepsze dla twojej sprawy bez głębszej wiedzy o tym, co robisz. Prawdopodobnie najłatwiejszym/uniwersalnym sposobem jest użycie wątków. Spójrz na this question po kilka pomysłów.

10

UPDATE: wziąć to z przymrużeniem oka, jako że jestem w kontakcie z najnowszych osiągnięć pyton asynchronicznych, w tym gevent i asyncio a nie rzeczywiście mają poważne doświadczenie z kodu asynchronicznego.


Istnieją 3 typowe metody gwintowe mniej kodowania asynchroniczny w Pythonie

  1. oddzwaniania - brzydki ale wykonalne Twisted Czyni to również.

  2. Generatory - ładne, ale wymagają wszystkie kodu, aby postępować zgodnie ze stylem.

  3. Użyj implementacji Pythona z prawdziwymi zadaniami - Stackless (RIP) i greenlet.

Niestety, najlepiej cały program powinien używać jednego stylu, lub rzeczy stają się skomplikowane. Jeśli wszystko jest w porządku, jeśli biblioteka udostępnia w pełni synchroniczny interfejs, prawdopodobnie masz problem, ale jeśli chcesz, aby kilka połączeń do biblioteki działało równolegle, w szczególności równolegle z innym kodem asynchronicznym, potrzebujesz wspólnego zdarzenia "reaktor". "to może działać z całym kodem.

Więc jeśli masz (lub spodziewasz się, że użytkownik ma) inny kod asynchroniczny w aplikacji, przyjęcie tego samego modelu jest prawdopodobnie sprytne.

Jeśli nie chcesz zrozumieć całego bałaganu, rozważ użycie starych, złych wątków. Są też brzydcy, ale pracują ze wszystkim innym.

Jeśli chcesz zrozumieć, w jaki sposób ci pomagają - i jak mogą ci to komplikować, to dobrze.

Greenlets może być najczystszym sposobem, jeśli możesz użyć rozszerzenia. Nie mam z nimi żadnego doświadczenia, więc nie mogę powiedzieć wiele.

2

Musisz również wykonać funkcję async jako funkcję foo. Co powiesz na to podejście?

@make_async 
def foo(somearg, callback): 
    # This function is now async. Expect a callback argument. 
    ... 

    # change 
    #  x = sync_call1(somearg, some_other_arg) 
    # to the following: 
    x = yield async_call1, somearg, some_other_arg 
    ... 

    # same transformation again 
    y = yield async_call2, x 
    ... 

    # change 
    #  return bar 
    # to a callback call 
    callback(bar) 

I make_async można zdefiniować tak:

def make_async(f): 
    """Decorator to convert sync function to async 
    using the above mentioned transformations""" 
    def g(*a, **kw): 
     async_call(f(*a, **kw)) 
    return g 

def async_call(it, value=None): 
    # This function is the core of async transformation. 

    try: 
     # send the current value to the iterator and 
     # expect function to call and args to pass to it 
     x = it.send(value) 
    except StopIteration: 
     return 

    func = x[0] 
    args = list(x[1:]) 

    # define callback and append it to args 
    # (assuming that callback is always the last argument) 

    callback = lambda new_value: async_call(it, new_value) 
    args.append(callback) 

    func(*args) 

UWAGA: Nie testowałem tego