Title: Przetwarzanie równoległe i strumieniowe

PRS

Przetwarzanie równoległe i strumieniowe

Wykład 8

Korutyny i async w Python

Yield

Słowo kluczowe yield w Pythonie zamienia zwykłą funkcję w generator, który na żądanie generuje sekwencję wartości zamiast obliczać je wszystkie naraz.

Funkcje Pythona nie zawsze mają polecenie return. Funkcje generatora to funkcje, które mają słowo kluczowe yield zamiast return.

Yield

Funkcja wykonuje się całkowicie przed zwróceniem. Możliwe są wielokrotne iteracje, ale wymagają przechowywania całej sekwencji.

Generator wstrzymuje się po ustąpieniu, wznawia, gdy żądana jest następna wartość. Zaprojektowany do iteracji jednoprzebiegowej, bardziej wydajny w przypadku dużych lub nieskończonych sekwencji.

Yield - przykład

def find_letter_occurrences(words, letter):
    for word in words:
        yield word.count(letter)
words = ["apple", "banana", "cherry"]
letter = "a"
output = find_letter_occurrences(words, letter)
print(output)

generator object find_letter_occurrences at 0x102935e00>

Yield - przykład

print(next(output))
print(next(output))
print(next(output))
print(next(output))

1
3
0
Traceback (most recent call last):
  ...
StopIteration

Yield - przykład

Wbudowana funkcja next() jest jednym ze sposobów pobierania następnej wartości z iteratora.

Wyjątek StopIteration sygnalizuje zakończenie procesu iteracji.

Funkcje generatora tworzą iteratory generatora, a iteratory są iterowalne.

for value in output:
    print(value)

Przetwarzanie asynchroniczne

Przetwarzanie asynchroniczne

W przeciwieństwie do tradycyjnego programowania synchronicznego, w którym zadania są wykonywane sekwencyjnie, programowanie asynchroniczne pozwala na wstrzymywanie i wznawianie zadań, co prowadzi do lepszego wykorzystania zasobów i szybszej reakcji.

Programowanie asynchroniczne w Pythonie oferuje potężny sposób na poprawę wydajności aplikacji, zwłaszcza w przypadku operacji związanych z wejściem/wyjściem i dużym opóźnieniem.

Przetwarzanie asynchroniczne

Sekwencyjność: Jako przykład możemy podać pieczenie ciasta, przygotowanie zajmuje nam czas i możemy skupiać się na wykonywaniu zadań od zebrania składników do wyjęcia ciasta z pieca.

Korutyny: W przypadku, gdy "ciasto wyrasta" lub piecze się w piekarniku możemy przygotowywać inne potrawy "przełączając" się aktywnie między zadaniami. Zmiana zadania sygnalizowana jest np przez dźwięk zakończenia pieczenia.

Korutyny

Korutyny

Korutyny (jeśli chodzi o Pythona) historycznie były projektowane jako rozszerzenie Generatorów.

Korutyny to komponenty programów komputerowych, które uogólniają podprogramy do wielozadaniowości bez wywłaszczania, umożliwiając wstrzymywanie i wznawianie wykonywania.

Korutyny

Dlaczego warto używać Korutyn?

Ponieważ korutyny mogą wstrzymywać i wznawiać kontekst wykonywania, są one dobrze przystosowane do przetwarzania współbieżnego, ponieważ umożliwiają programowi określenie, kiedy „przełączyć się na kontekst” z jednego punktu kodu do drugiego.

Korutyny

Korutynę definiujemy za pomocą async def:

async def coro(n):
    pass


Funkcje zdefiniowane za pomocą async def są zawsze korutynami, nawet jeśli nie zawierają wyrażeń await.
Zwykłe generatory po wywołaniu zwracają obiekt generatora; podobnie coroutine zwracają obiekt coroutine.
Wyjątki StopIteration nie są propagowane poza coroutine i są zastępowane przez RuntimeError.

Korutyny

Korutyny działają jako współbieżne podzadania we współpracy z modułem asyncio.

Korutyny służą do tworzenia współbieżnych aplikacji i w przeciwieństwie od wątków i procesów są powszechnie stosowane w Python.

Gdy wywołujemy korutynę, kod wewnątrz funkcji nie jest wykonywany od razu. Zamiast tego zwracany jest obiekt coroutine, co oznacza, że ​​coroutine została zdefiniowana, ale jeszcze nie została uruchomiona

Korutyny - definicja

Preferowanym sposobem pisania aplikacji asyncio są współprogramy zadeklarowane ze składnią async/await. Mówimy, że obiekt jest obiektem awaitable, jeśli można go użyć w wyrażeniu await. Istnieją trzy główne typy obiektów odpowiadających na await: korutyny, zadania (tasks) i future.

Korutyny - przykład

async def coroutine_add(number: int, number2: int) -> int:
    return number + number2

def add(number: int, number2: int) -> int:
    return number + number2

function_result = add(1,1)
coroutine_result = coroutine_add(1,1)

print(f'Function result is {function_result} and the type is {type(function_result)}')
print(f'Coroutine result is {coroutine_result} and the type is {type(coroutine_result)}')

Function result is 2 and the type is <class 'int'>
Coroutine result is <coroutine object coroutine_add at 0x7f588ffd17a0> and the type is
<class 'coroutine'>

Korutyny - definicja

Korutyna bez await nie zostanie wykonana

import asyncio

async def nested():
    return 42

async def main():
    # Nothing happens if we just call "nested()".
    # A coroutine object is created but not awaited,
    # so it *won't run at all*.
    nested()

    # Let's do it differently now and await it:
    print(await nested())  # will print "42".

await main()

Event loop

Event Loop: Pętla zdarzeń jest rdzeniem każdej aplikacji asyncio. Pętle zdarzeń uruchamiają asynchroniczne zadania i wywołania zwrotne, wykonują operacje wejścia/wyjścia sieciowego i uruchamiają podprocesy. Aby uzyskać pętlę zdarzeń dla bieżącego kontekstu, użyj get_event_loop().

Event loop

Obiekt coroutine można zaplanować i wykonać później za pomocą event loop.

Event loop jest podstawowym elementem programowania asynchronicznego w Pythonie.

Event loop zapewnia, że ​​program pozostaje responsywny, obsługując te operacje bez blokowania głównego przepływu wykonywania. Koordynując wykonywanie coroutines, event loop ułatwia współbieżne przetwarzanie zadań.

Korutyny - asyncio

Korutyny - asyncio

Support for generator-based coroutines is deprecated and is scheduled for removal in Python 3.10. Use async def instead.

Generator-based coroutines predate async/await syntax. They are Python generators that use yield from expressions to await on Futures and other coroutines.

Generator-based coroutines should be decorated with @asyncio.coroutine, although this is not enforced.

Korutyny - definicja

Aby wykonać korutyny, używamy konkretnych narzędzi dostarczonych przez bibliotekę asyncio języka Python. Funkcja asyncio.run() to prosty sposób na rozpoczęcie pętli zdarzeń i uruchomienie naszej głównej korutyny, podczas gdy słowo kluczowe await pozwala nam wstrzymywać i wznawiać coroutine w jej obrębie.

Korutyny - definicja

Korutyny - definicja

Funkcja asyncio.run(process_data()) stanowi punkt początkowy procesu asyncio, który uruchamia pętlę zdarzeń i wykonuje naszą główną korutynę.

Wewnątrz process_data używamy await do wywołania fetch_data i compute_result. Gdy używane jest słowo kluczowe await, coroutine zatrzymuje się, umożliwiając uruchomienie innych operacji. Gdy oczekiwane zadanie zostanie ukończone, wykonywanie jest wznawiane od miejsca, w którym zostało przerwane.

Korutyny - asyncio_create

Create_task przekazuje korutyne do uruchomienia „w tle”, tj. równocześnie z bieżącym zadaniem i wszystkimi innymi zadaniami, przełączając się między nimi w punktach await. Zwraca uchwyt zwany „zadaniem”, którego można również użyć do anulowania wykonania coroutine.

Używając zadań (tasks), pozwalamy event loop zarządzać wieloma operacjami równolegle, dzięki czemu nasz program jest bardziej wydajny i responsywny. W scenariuszach z rozległymi zadaniami oczekującymi lub zadaniami wejścia/wyjścia, takie podejście może prowadzić do znacznych ulepszeń wydajności.

Korutyny - definicja

Total time 3.0033 seconds

Asyncio

Asyncio to tylko jedna z kilku bibliotek Pythona do programowania asynchronicznego:

Twisted: Twisted to oparta na Pythonie struktura sieciowa typu open source, która umożliwia łatwe tworzenie serwerów SMTP, HTTP, proxy i SSH (między innymi). Działa asynchronicznie i w sposób sterowany zdarzeniami, umożliwiając aplikacjom reagowanie na różne połączenia sieciowe bez polegania na konwencjonalnych modelach wątków.
Tornado: Tornado to asynchroniczna biblioteka sieciowa i struktura internetowa dla Pythona. W przeciwieństwie do wielu innych struktur internetowych Pythona, Tornado nie polega na WSGI i zazwyczaj wymaga tylko jednego wątku na proces do uruchomienia.

Asyncio

Asyncio to tylko jedna z kilku bibliotek Pythona do programowania asynchronicznego:

Trio: Trio to współczesna biblioteka Pythona, która ułatwia tworzenie aplikacji asynchronicznych. Są to programy, które wykonują wiele zadań jednocześnie, paralelizując operacje wejścia/wyjścia, takie jak pająk sieciowy, który pobiera wiele stron jednocześnie lub serwer internetowy zarządzający kilkoma równoczesnymi pobraniami.
Curio: Curio to biblioteka do współbieżnego programowania systemowego w Pythonie, która działa przy użyciu korutyn. Oferuje typowe abstrakcje programistyczne, takie jak zadania, gniazda, pliki, blokady i kolejki.

Promises

Promise to obiekt, który jest skojarzony z zadaniem asynchronicznym w językach jak JavaScript. Funkcja asynchroniczna ma uruchamiać pracę w tle i natychmiast zwracać promise, bez czekania na zakończenie tej pracy. Następnie wywołujący może użyć obiektu promise, aby określić, kiedy praca została ukończona.

W JavaScript funkcja asynchroniczna jest definiowana jako funkcja zwracająca promise.

W przeciwieństwie do Pythona, asynchroniczne funkcje JavaScript są normalnymi funkcjami. Jeśli je wywołasz, zostaną wykonane.

Promises - przykład

let myPromise = new Promise(function(resolve, reject) {
  // some code that takes time, like loading data
  let success = true; // change this to false to check error

  if (success) {
    resolve("The data has loaded successfully!");
  } else {
    reject("There was an error loading the data.");
  }
});

myPromise.then(function(message) {
// This runs if the promise is fulfilled
  console.log(message);
}).catch(function(error) {
// This runs if the promise is rejected
  console.log(error);
});

W Pythonie ma ma promisów!

Podsumowanie