2.32. Coroutine’ler

Bir coroutine, ortasında duraklayıp daha sonra kaldığı yerden, tüm yerel değişkenleri korunmuş halde devam edebilen bir fonksiyondur. Generator desenini yansıtır – askıya alınıp devam ettirilebilen bir fonksiyon gövdesi – ancak her devam ettirmeyi kimin sürdüğü konusunda bir fark vardır.

Python’da bir coroutine yazmak için kullanılan sözdizimi async / await anahtar kelime çiftidir. async fonksiyonu bir coroutine olarak işaretler; await ise içerideki duraklamaya izin verilen noktaları işaretler.

2.32.1. Bir coroutine tanımlamak

async def ile tanımlanan bir fonksiyon, bir coroutine fonksiyonudur. Onu çağırmak gövdeyi çalıştırmaz; henüz başlamamış bir coroutine nesnesi döndürür:

async def greet(name):
    print("hello,", name)

coro = greet("Alice")        # body NOT run yet

Coroutine nesnesi, fonksiyonun en başında duraklatılmış durumdadır. Gövdenin çalışması için bir şeyin onu sürmesi gerekir – o şey, bir çalışma zamanı bileşeni olan event loop’tur. MicroPython, asyncio modülünde bir tane sağlar. Şimdilik coroutine’i “çalışmaya hazır, bir sürücü bekliyor” olarak düşünün.

2.32.2. Bir coroutine içinde duraklamak

Bir coroutine içinde, bir await ifadesi, beklenen değer hazır olana kadar yürütmeyi askıya alır:

async def fetch_and_log():
    data = await read_sensor()
    print("got:", data)

Gövde await read_sensor() ifadesine ulaştığında, coroutine kontrolü onu çalıştıran her neyse ona geri verir. read_sensor() tamamlandığında, sürücü coroutine’i bir sonraki satırda devam ettirir ve sonuç data değişkenine bağlanır.

await yalnızca bir coroutine içinde geçerlidir. Normal bir fonksiyonda kullanmak bir sözdizimi hatasıdır.

2.32.3. Generator’larla ilişkisi

Coroutine’ler ve generator’lar aynı temel mekanizmayı paylaşır. Ayrım, her devam ettirmeyi kimin çektiğindedir:

  • Bir generator değerler üretir; tüketici bir sonrakini next() ile veya yineleme yaparak çeker.

  • Bir coroutine kontrol üretir; bir event loop, beklenen işlem hazır olduğunda devam ettirmeyi zamanlar.

Generator-yield el sıkışması anlamlı geliyorsa, coroutine el sıkışması da aynı fikirdir – yalnızca bir for döngüsü yerine bir event loop tarafından sürülür.

Bir event loop, bir şeyi (bir zamanlayıcıyı, bir ağ olayını, başka bir coroutine’in bitmesini) bekleyen coroutine’lerin bir listesini tutan küçük bir görevlendiricidir. Her yinelemede, beklemesi karşılanmış bir coroutine seçer, onu bir sonraki await‘e kadar devam ettirir, ardından o coroutine’in şimdi neyi beklediğini kaydeder ve başka hazır bir coroutine’e geçer. Sonuç, tek bir iş parçacığı üzerinde eş zamanlı olarak ilerleyen birçok görevdir – her coroutine, await noktalarında kontrolü gönüllü olarak bırakır ve döngü, bu anları ilerleme kaydetmeye hazır diğer coroutine’lerle doldurur.

Kaputun altında, await ve yield, bir fonksiyonu askıya almak ve devam ettirmek için aynı Python çalışma zamanı özelliğini kullanır. Anahtar kelimeler farklıdır çünkü etraflarındaki kural da farklıdır: yield, next() ile çeken bir tüketiciye bir değer geri verir; await, beklenen işlem hazır olduğunda devam ettirmeyi zamanlayan bir event loop’a kontrol verir. async / await, esasen coroutine deseni için daha yeni bir sözdizimidir – eski kütüphaneler, coroutine’ler arasında askıya almayı devretmek için yield from (Yineleyiciler ve generator’lar sayfasında tanıtılmıştır) kullanarak coroutine’leri doğrudan generator mekanizması üzerine inşa ediyordu.

2.32.4. Coroutine’lerin bir sürücüye ihtiyacı vardır

Bir coroutine, onu sürecek bir çalışma zamanı olmadan hareketsizdir. Bir tane tanımlamak sorun değildir; ama bir tanesini çalıştırmak bir event loop gerektirir. MicroPython’ın asyncio modülü o event loop’u sağlar. Asyncio bölümü; döngünün nasıl başlatılacağını, coroutine’lerin üzerinde nasıl zamanlanacağını, kilitler ve olaylarla aralarında durumun nasıl paylaşılacağını, iptal ve zaman aşımlarının nasıl ele alınacağını ve burada tanıtılan async / await anahtar kelimeleri etrafında gerçek bir uygulamanın nasıl şekillendirileceğini anlatır.