Programowanie asynchroniczne - przydatne linki

0

Cześć wszystkim

Kiedyś próbowałem zaimplementować usługę serwera FTP + klienta, ale nie jestem zadowolony z efektów swojej pracy dlatego teraz chcę zrobić to raz jeszcze od zera. Poczytałem dość dużo na temat działania wątków i dowiedziałem się, że dla usług serwerowych te wątki znacząco zaśmiecają pamięć. Jeśli wziąłbym pod uwagę podejście w oparciu o wielowątkowość to z każdym połączeniem rosłaby liczba stworzonych wątków w systemie - nie chcę żeby tak było.

Więc podjąłem próbę zapoznania się z modułem asyncio. Niestety dokumentacja okazała się dla mnie mało zrozumiała. Chciałbym zrozumieć programowanie asynchroniczne również od strony teoretycznej, żeby nie klepać kodu bezmyślnie. Rzecz jasna znam zalety takiego podejścia, ale to za mało jeśli nie wiem za co odpowiada poszczególna funkcja w wyżej wymienionym module.

Czy ktoś próbował swoich sił w takim podejściu? Jeśli tak to prosiłbym o jakieś linki, w których w miarę po ludzku byłaby wytłumaczona teoria.

Z góry dzięki i pozdrawiam!

2

asyncio nadal mocno się rozwija i z wersji na wersje implementowane są nowe funkcjonalności, bądź optymalizowane są istniejące rozwiązania (np wprowadzenie syntaxu async def w Pytonie 3.5, wprowadzenie wrappera asyncio.run() w Pythonie 3.7). Z tego powodu większość tutoriali bywa nieaktualna. Dokumentacja (docs.python.org/3/library/asyncio.html) jest najbardziej aktualnym źródłem informacji.

Z własnego doświadczenia, główne elementy asyncio które wykorzystywałem w produkcyjnym kodzie to:

  • definicja asynchronicznej funkcji
async def func():
    pass
  • wywołanie asynchronicznej funkcji i czekanie na wynik
result = await func()
  • wywołanie kilku funkcji "równolegle", gdy func1 i func2 to funkcje asynchroniczne. Tworzenie coroutines + użycie asyncio.gather zazwyczaj znajduje się w main
coros = [func1(), func2()]
results = await asyncio.gather(*coros)
  • wystartowanie asynchronicznego main
result = asyncio.run(main())

Przed wprowadzeniem asyncio.run(), podobny efekt uzyskiwaliśmy z

loop = asyncio.new_event_loop()
result = loop.run_until_complete(main())

asyncio.run zapewnia dodatkowo obsługę anulowania funkcji i jest preferowanym sposobem startowania main (docs.python.org/3/library/asyncio-task.html#asyncio.run)

  • wywołanie funkcji z blokującym IO, asyncio.run_in_executor powoduje stworzenie osobnego wątku i wywołanie tam blokującej funkcji
def blocking():
    time.sleep(60)
    return 42

async def non_blocking():
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(
        None,
        lambda: blocking()
    )

Dzięki użyciu lambdy możemy wywołać blokującą funkcję z keyword arguments.

A tutaj prosty przykład serwera: docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever

1 użytkowników online, w tym zalogowanych: 0, gości: 1