8.5. Zaman aşımları ve iptal¶
İptal, asyncio’nun “bu eşyordamı çalıştırmayı durdur, temizlik yapma şansı bulması için içinde bir istisna oluştur ve onu zamanlayıcıdan çıkar” işlemine verdiği isimdir. Zaman aşımları, bir şeyi iptal etmenin en yaygın nedenidir; manuel task.cancel() ise diğeridir.
8.5.1. Bir görevi iptal etme¶
Task.cancel çağrısı, çalışan eşyordamın içinde bir sonraki await noktasında asyncio.CancelledError oluşturulmasını zamanlar. Eşyordam, iptali görmezden gelmekte (istisnayı yakalayıp çalışmaya devam etmekte) veya ona uymakta serbesttir – her zamanki tercih, temizleme kodunu çalıştırdıktan sonra ona uymaktır:
async def network_worker(stream):
try:
while True:
data = await stream.read(64)
# ...
finally:
stream.close()
Çıplak bir finally yan tümcesi en temiz desendir: temizlik, eşyordamın normal şekilde çıkması, ilgisiz bir istisna oluşturması veya iptal edilmesi durumunda çalışır. CancelledError, finally aracılığıyla yukarı doğru geri yayılır, döngü görevin bittiğini görür ve await task‘in çağırıcısı iptali görür.
CancelledError‘i açıkça yakalamak da, uygulama onunla belirli bir şey yapmak istediğinde sorun değildir – onu günlüğe kaydetmek, kaynağı temiz bir şekilde devretmek vb. Kural şudur: temizlik çalıştıktan sonra onu yeniden oluştur. CancelledError‘i yutmak, çağırıcısı durmasını istediğinde eşyordamı canlı tutar; bu neredeyse her zaman bir hatadır:
async def worker():
try:
await long_running_thing()
except asyncio.CancelledError:
log("worker cancelled, cleaning up")
close_resources()
raise # << this line is the important one
8.5.2. wait_for ile zaman aşımları¶
asyncio.wait_for(), bir beklenebiliri bir son tarihle sarmalar. Beklenebilir, zaman aşımı içinde biterse sonucu döndürülür. Bitmezse, beklenebilir iptal edilir ve çağıran asyncio.TimeoutError alır:
try:
frame = await asyncio.wait_for(grab_frame(), timeout=2)
except asyncio.TimeoutError:
print("camera took too long")
Saniye argümanı bir kayan noktalı sayı kabul eder. Milisaniye biçimindeki son tarihler için asyncio.wait_for_ms(), bir tamsayı milisaniye sayısı alır – aygıt yazılımının (firmware) milisaniye ayrıntılı zamanlama ayarlarıyla uyumlu bir MicroPython uzantısıdır.
Dahili olarak, wait_for tam olarak manuel iptalin yapacağı şeyi yapar: son tarih dolduğunda, sarmalanan görevde cancel() çağrısını yapar, eşyordamın içinde CancelledError oluşturulur, finally yan tümceleri çalışır ve temizlik tamamlandığında istisna, çağıran için bir TimeoutError‘e çevrilir.
Bu, CancelledError‘i yakalayıp görmezden gelen bir eşyordamın zaman aşımını boşa çıkaracağı anlamına gelir – son tarih doldu, ancak eşyordam durmayı reddetti ve wait_for onu zorlayamaz. Önceki kural burada da geçerlidir: CancelledError‘i yalnızca temizlik çalıştırmak için yakala, sonra yeniden oluştur.
8.5.3. Gather aracılığıyla iptal¶
İptal, gather() aracılığıyla aşağı doğru yayılır. Bir gather çağrısını bekleyen görev iptal edilirse, gather içinde hâlâ çalışan her beklenebilir de iptal edilir – her biri, iptal çağırıcıya yükselmeden önce kendi finally yan tümceleri aracılığıyla temizlik yapma şansı bulur.
Zaman aşımlarıyla birleştirildiğinde, bu, bir işlemler grubuna son tarih koymanın standart yoludur:
await asyncio.wait_for(
asyncio.gather(a(), b(), c()),
timeout=5,
)
Ya her alt işlem beş saniye içinde biter ya da hepsi birlikte iptal edilir.
8.5.4. Bir uygulamayı kapatma¶
İptal aynı zamanda gerçek bir uygulamanın temiz bir şekilde durma yöntemidir. Desen betikler arasında tutarlıdır: main, başlattığı uzun ömürlü arka plan görevlerinin tutamaçlarını yakalar, en üst düzey işini çalıştırır, ardından her tutamacı iptal eder ve onu bir finally bloğunda bekler:
async def main():
sender = asyncio.create_task(uplink())
watcher = asyncio.create_task(button_watcher())
try:
await snapshot_loop()
finally:
sender.cancel()
watcher.cancel()
await asyncio.gather(sender, watcher,
return_exceptions=True)
return_exceptions=True, gather’ın her alt görevin teslim etmek üzere olduğu CancelledError‘i yeniden oluşturmasını önleyen hiledir, böylece uygulamanın kendi çıkış nedeni – snapshot_loop‘un oluşturduğu veya oluşturmadığı her ne ise – main‘den dışarı çıkan şeydir.