Python'ning asyncio Futures imkoniyatlarini o'zlashtiring. Mustahkam, yuqori unumdorlikka ega ilovalarni yaratish uchun past darajadagi asinxron tushunchalar, amaliy misollar va ilg'or usullarni o'rganing.
Asyncio Futures ochildi: Python dasturlash tilida past darajadagi asinxron dasturlashga chuqur sho'ng'ish
Zamonaviy Python dasturlash olamida async/await
sintaksisi yuqori unumdorlikka ega, I/U bilan bog'langan ilovalarni yaratish uchun asosiy vositaga aylandi. U ketma-ket ko'rinadigan konkurent kodni yozishning toza, oqlangan usulini taqdim etadi. Ammo bu yuqori darajadagi sintaktik "shakar" ostida kuchli va fundamental mexanizm yotadi: Asyncio Future. Siz har kuni xom Futures bilan aloqa qilmasligingiz mumkin bo'lsa-da, ularni tushunish Python-da asinxron dasturlashni chinakam o'zlashtirishning kalitidir. Bu avtomobil dvigatelining qanday ishlashini o'rganishga o'xshaydi; haydash uchun bilishingiz shart emas, lekin agar siz mohir mexanik bo'lishni istasangiz, bu muhimdir.
Ushbu keng qamrovli qo'llanma asyncio
haqidagi pardani ochib beradi. Biz Futures nima ekanligini, ularning korutinlar va vazifalardan qanday farq qilishini va nima uchun bu past darajadagi ibtido Pythonning asinxron imkoniyatlari qurilgan poydevor ekanligini o'rganamiz. Murakkab poyga holatini tuzatayotgan bo'lasizmi, eski callback-ga asoslangan kutubxonalar bilan integratsiya qilyapsizmi yoki shunchaki asinxronni chuqurroq tushunishni maqsad qilgan bo'lasizmi, ushbu maqola siz uchun.
Asyncio Future o'zi nima?
Asosida asyncio.Future
- bu asinxron operatsiyaning yakuniy natijasini ifodalovchi obyektdir. Uni vaqtinchalik belgi, va'da yoki hali mavjud bo'lmagan qiymat uchun kvitansiya sifatida o'ylang. Tugallanishi uchun vaqt kerak bo'ladigan operatsiyani boshlaganingizda (masalan, tarmoq so'rovi yoki ma'lumotlar bazasi so'rovi), darhol Future obyekti qaytarilishi mumkin. Dasturingiz boshqa ishlarni qilishni davom ettirishi mumkin va operatsiya nihoyat tugagach, natija (yoki xato) ushbu Future obyekti ichiga joylashtiriladi.
Foydali real dunyo analogiyasi - gavjum kafeda kofe buyurtma qilish. Siz buyurtma berasiz va to'laysiz, barista sizga buyurtma raqami yozilgan kvitansiya beradi. Sizda hali kofe yo'q, lekin sizda kvitansiya bor - kofening va'dasi. Endi siz stol topish yoki peshtaxtada bekor turish o'rniga telefoningizni tekshirishingiz mumkin. Kofe tayyor bo'lgach, raqamingiz aytiladi va siz kvitansiyangizni yakuniy natija uchun "qaytarib olishingiz" mumkin. Kvitansiya - bu Future.
Future-ning asosiy xususiyatlari quyidagilarni o'z ichiga oladi:
- Past daraja: Futures vazifalar bilan solishtirganda ibtidoiy qurilish blokidir. Ular qanday qilib kodni ishga tushirishni bilishmaydi; ular shunchaki keyinroq o'rnatiladigan natija uchun konteynerlardir.
- Kutiladigan: Future-ning eng muhim xususiyati shundaki, u kutiladigan obyektdir. Bu shuni anglatadiki, siz unda
await
kalit so'zidan foydalanishingiz mumkin, bu Future natijaga ega bo'lmaguncha korutiningizning bajarilishini to'xtatib turadi. - Holatli: Future o'zining hayot aylanishi davomida bir nechta alohida holatlardan birida mavjud: Kutilayotgan, Bekor qilingan yoki Tugatilgan.
Futures vs. Korutinlar vs. Vazifalar: Chalkashlikni aniqlashtirish
asyncio
bilan yangi ishlaydigan dasturchilar uchun eng katta to'siqlardan biri bu uchta asosiy tushuncha o'rtasidagi munosabatni tushunishdir. Ular chuqur o'zaro bog'liq, lekin turli maqsadlarga xizmat qiladi.
1. Korutinlar
Korutin - bu shunchaki async def
bilan belgilangan funksiya. Korutin funksiyasini chaqirganda, u o'z kodini bajarmaydi. Buning o'rniga u korutin obyektini qaytaradi. Ushbu obyekt hisoblash uchun asosdir, lekin voqea halqasi tomonidan boshqarilmaguncha hech narsa sodir bo'lmaydi.
Misol:
async def fetch_data(url): ...
fetch_data("http://example.com")
-ni chaqirish sizga korutin obyektini beradi. Siz uni await
qilmaguningizcha yoki uni Task sifatida rejalashtirmaguningizcha, u inertdir.
2. Vazifalar
asyncio.Task
- bu korutinni voqea halqasida bir vaqtning o'zida ishga tushirish uchun ishlatadigan narsangiz. Siz asyncio.create_task(my_coroutine())
yordamida Task yaratasiz. Task korutiningizni o'rab oladi va voqea halqasi imkoniyatga ega bo'lishi bilanoq uni "fon rejimida" ishga tushirishni darhol rejalashtiradi. Bu erda tushunish kerak bo'lgan eng muhim narsa shundaki, Task - bu Future sinfining kichik sinfidir. Bu korutinni qanday haydashni biladigan ixtisoslashgan Future.
O'ralgan korutin tugallanganda va qiymatni qaytarganda, Task (esda tuting, bu Future) avtomatik ravishda natijaga ega bo'ladi. Agar korutin istisno keltirib chiqarsa, Taskning istisnosiga o'rnatiladi.
3. Futures
Oddiy asyncio.Future
undan ham fundamentaldir. Taskdan farqli o'laroq, u biron bir aniq korutinga bog'langan emas. Bu shunchaki bo'sh joy egasi. Boshqa narsa - kodingizning boshqa qismi, kutubxona yoki voqea halqasining o'zi - keyinchalik uning natijasini yoki istisnosini aniq belgilash uchun javobgardir. Tasks bu jarayonni siz uchun avtomatik ravishda boshqaradi, lekin xom Future bilan boshqaruv qo'lda bo'ladi.
Farqni aniqroq qilish uchun qisqacha jadval:
Tushuncha | Bu nima | Qanday yaratiladi | Asosiy foydalanish holati |
---|---|---|---|
Korutin | async def bilan belgilangan funksiya; generatorga asoslangan hisoblash asosi. |
async def my_func(): ... |
Asinxron mantiqni aniqlash. |
Task | Voqea halqasida korutinni o'rab oladigan va ishga tushiradigan Future kichik sinfi. | asyncio.create_task(my_func()) |
Korutinlarni bir vaqtning o'zida ishga tushirish ("yoq va unuting"). |
Future | Yakuniy natijani ifodalovchi past darajadagi kutiladigan obyekt. | loop.create_future() |
Callback-ga asoslangan kod bilan interfeys; moslashtirilgan sinxronlash. |
Xulosa qilib aytganda: Siz Korutinlarni yozasiz. Siz ularni Tasks yordamida bir vaqtning o'zida ishga tushirasiz. Tasks ham, asosiy I/U operatsiyalari ham yakunlanishni signal berish uchun asosiy mexanizm sifatida Futures-dan foydalanadi.
Future-ning hayot aylanishi
Future oddiy, ammo muhim holatlar to'plamidan o'tadi. Ushbu hayot aylanishini tushunish ulardan samarali foydalanishning kalitidir.
1-holat: Kutilayotgan
Future birinchi marta yaratilganda, u kutilayotgan holatda bo'ladi. Unda natija ham, istisno ham yo'q. U kimdir uni tugatishini kutmoqda.
import asyncio
async def main():
# Joriy voqea halqasini oling
loop = asyncio.get_running_loop()
# Yangi Future yarating
my_future = loop.create_future()
print(f"Future tugallanganmi? {my_future.done()}") # Natija: False
# Asosiy korutinni ishga tushirish uchun
asyncio.run(main())
2-holat: Tugatish (Natija yoki Istisno o'rnatish)
Kutilayotgan Future ikki usuldan birida tugatilishi mumkin. Bu odatda natijaning "ishlab chiqaruvchisi" tomonidan amalga oshiriladi.
1. set_result()
bilan muvaffaqiyatli natijani o'rnatish:
Asinxron operatsiya muvaffaqiyatli tugallanganda, uning natijasi ushbu usul yordamida Future-ga biriktiriladi. Bu Future-ni tugatilgan holatga o'tkazadi.
2. set_exception()
bilan istisnoni o'rnatish:
Agar operatsiya bajarilmasa, istisno obyekti Future-ga biriktiriladi. Bu Future-ni tugatilgan holatga ham o'tkazadi. Boshqa korutin ushbu Future-ni await
qilganda, biriktirilgan istisno keltirib chiqariladi.
3-holat: Tugatilgan
Natija yoki istisno o'rnatilgandan so'ng, Future bajarilgan deb hisoblanadi. Uning holati endi yakuniy va o'zgartirilishi mumkin emas. Buni future.done()
usuli bilan tekshirishingiz mumkin. Ushbu Future-ni await
qilayotgan har qanday korutinlar endi uyg'onadi va bajarilishini davom ettiradi.
(Ixtiyoriy) 4-holat: Bekor qilingan
Kutilayotgan Future-ni future.cancel()
usulini chaqirish orqali ham bekor qilish mumkin. Bu operatsiyadan voz kechish uchun so'rovdir. Agar bekor qilish muvaffaqiyatli bo'lsa, Future bekor qilingan holatga o'tadi. Kutganda, bekor qilingan Future CancelledError
-ni keltirib chiqaradi.
Futures bilan ishlash: Amaliy misollar
Nazariya muhim, lekin kod uni haqiqiy qiladi. Xom Futures-dan muayyan muammolarni hal qilish uchun qanday foydalanish mumkinligini ko'rib chiqaylik.
1-misol: Qo'lda ishlab chiqaruvchi/iste'molchi stsenariysi
Bu asosiy aloqa namunasini ko'rsatadigan klassik misol. Bizda Future-ni kutadigan bitta korutin (`iste'molchi`) va ba'zi ishlarni bajaradigan va keyin ushbu Future-ga natijani o'rnatadigan boshqa korutin (`ishlab chiqaruvchi`) bo'ladi.
import asyncio
import time
async def producer(future):
print("Ishlab chiqaruvchi: Og'ir hisoblash ustida ishlashni boshlash...")
await asyncio.sleep(2) # I/U yoki CPU-intensive ishni simulyatsiya qiling
result = 42
print(f"Ishlab chiqaruvchi: Hisoblash tugallandi. Natijani o'rnatish: {result}")
future.set_result(result)
async def consumer(future):
print("Iste'molchi: Natijani kutish...")
# 'await' kalit so'zi bu erda iste'molchini to'xtatib turadi, future bajarilmaguncha
result = await future
print(f"Iste'molchi: Natijani oldi! Bu {result}")
async def main():
loop = asyncio.get_running_loop()
my_future = loop.create_future()
# Ishlab chiqaruvchini fonda ishlashga rejalashtiring
# U my_future-ni tugatish ustida ishlaydi
asyncio.create_task(producer(my_future))
# Iste'molchi ishlab chiqaruvchining future orqali tugashini kutadi
await consumer(my_future)
asyncio.run(main())
# Kutilgan natija:
# Iste'molchi: Natijani kutish...
# Ishlab chiqaruvchi: Og'ir hisoblash ustida ishlashni boshlash...
# (2 soniyalik pauza)
# Ishlab chiqaruvchi: Hisoblash tugallandi. Natijani o'rnatish: 42
# Iste'molchi: Natijani oldi! Bu 42
Ushbu misolda Future sinxronlash nuqtasi sifatida ishlaydi. `Iste'molchi` natijani kim berganini bilmaydi yoki qiziqtirmaydi; uni faqat Future qiziqtiradi. Bu ishlab chiqaruvchi va iste'molchini ajratadi, bu esa konkurent tizimlarda juda kuchli namuna hisoblanadi.
2-misol: Callback-ga asoslangan API-larni ko'prik qilish
Bu xom Futures uchun eng kuchli va keng tarqalgan foydalanish holatlaridan biridir. Ko'pgina eski kutubxonalar (yoki C/C++ bilan interfeysga muhtoj bo'lgan kutubxonalar) `async/await` native emas. Buning o'rniga ular callback-ga asoslangan uslubdan foydalanadilar, bu erda siz tugatilgandan so'ng bajariladigan funksiyani uzatasiz.
Futures ushbu API-larni modernizatsiya qilish uchun mukammal ko'prikni ta'minlaydi. Biz kutiladigan Future-ni qaytaradigan o'rash funksiyasini yaratishimiz mumkin.
Faraz qilaylik, bizda URL-ni oladigan va bajarilganda `callback(data)`-ni chaqiradigan gipotetik meros funksiyasi legacy_fetch(url, callback)
mavjud.
import asyncio
from threading import Timer
# --- Bu bizning gipotetik meros kutubxonamiz ---
def legacy_fetch(url, callback):
# Ushbu funksiya asinxron emas va callback-lardan foydalanadi.
# Biz threading modulidan taymer yordamida tarmoq kechikishini simulyatsiya qilamiz.
print(f"[Meros] {url}-ni olish... (Bu blokirovka uslubidagi qo'ng'iroq)")
def on_done():
data = f"{url}-dan ba'zi ma'lumotlar"
callback(data)
# 2 soniyalik tarmoq qo'ng'irog'ini simulyatsiya qiling
Timer(2, on_done).start()
# -----------------------------------------------
async def modern_fetch(url):
"""Meros funksiyasi atrofidagi kutiladigan o'rashimiz."""
loop = asyncio.get_running_loop()
future = loop.create_future()
def on_fetch_complete(data):
# Ushbu callback boshqa mavzuda bajariladi.
# Asosiy voqea halqasiga tegishli bo'lgan future-ga natijani xavfsiz o'rnatish uchun,
# biz loop.call_soon_threadsafe-dan foydalanamiz.
loop.call_soon_threadsafe(future.set_result, data)
# Meros funksiyasini maxsus callback bilan chaqiring
legacy_fetch(url, on_fetch_complete)
# Callback tomonidan yakunlanadigan future-ni kuting
return await future
async def main():
print("Zamonaviy olishni boshlash...")
data = await modern_fetch("http://example.com")
print(f"Zamonaviy olish yakunlandi. Qabul qilindi: '{data}'")
asyncio.run(main())
Ushbu namuna juda foydali. `Modern_fetch` funksiyasi barcha callback murakkabligini yashiradi. `Main` nuqtai nazaridan, bu shunchaki kutish mumkin bo'lgan oddiy `async` funksiyasi. Biz meros API-ni muvaffaqiyatli "futurizatsiya qildik".
Eslatma: `loop.call_soon_threadsafe` dan foydalanish, callback boshqa mavzu tomonidan bajarilganda juda muhim, chunki bu asyncio bilan integratsiya qilinmagan kutubxonalarda I/U operatsiyalari bilan keng tarqalgan. Bu `future.set_result` asyncio voqea halqasi kontekstida xavfsiz tarzda chaqirilishini ta'minlaydi.
Xom Futures-dan qachon foydalanish kerak (va qachon emas)
Mavjud kuchli yuqori darajadagi abstraksiyalar bilan Future kabi past darajadagi vositaga qachon murojaat qilish kerakligini bilish muhimdir.Xom Futures-dan quyidagi hollarda foydalaning:
- Callback-ga asoslangan kod bilan interfeys: Yuqoridagi misolda ko'rsatilgandek, bu asosiy foydalanish holati. Futures ideal ko'prik hisoblanadi.
- Moslashtirilgan sinxronlash ibtidolarini yaratish: Agar sizga muayyan xatti-harakatlarga ega bo'lgan Event, Lock yoki Queue o'z versiyasini yaratish kerak bo'lsa, Futures siz quradigan asosiy komponent bo'ladi.
- Natijani korutindan boshqa narsa ishlab chiqaradi: Agar natija tashqi voqea manbasi (masalan, boshqa jarayondan signal, websocket mijozidan xabar) tomonidan yaratilgan bo'lsa, Future asyncio olamida ushbu kutilayotgan voqeani ifodalashning mukammal usuli hisoblanadi.
Xom Futures-dan qoching (buning o'rniga Tasks-dan foydalaning):
- Siz shunchaki korutinni bir vaqtning o'zida ishga tushirmoqchisiz: Bu `asyncio.create_task()`-ning vazifasi. U korutinni o'rash, rejalashtirish va natijasini yoki istisnosini Task-ga (Future bo'lgan) tarqatish bilan ishlaydi. Bu erda xom Future-dan foydalanish g'ildirakni qayta ixtiro qilish bo'ladi.
- Konkurent operatsiyalar guruhlarini boshqarish: Bir nechta korutinni ishga tushirish va ularning tugashini kutish uchun `asyncio.gather()`, `asyncio.wait()` va `asyncio.as_completed()` kabi yuqori darajadagi API-lar ancha xavfsizroq, o'qishga osonroq va xatolarga kamroq moyil. Ushbu funksiyalar to'g'ridan-to'g'ri korutinlar va Tasks-da ishlaydi.
Ilg'or tushunchalar va tuzoqlar
Futures va voqea halqasi
Future o'zi yaratilgan voqea halqasi bilan chambarchas bog'liq. `Await future` ifodasi ishlaydi, chunki voqea halqasi ushbu aniq Future haqida biladi. U kutilayotgan Future-da `await` ko'rganda, joriy korutinni to'xtatib turishi va qilish uchun boshqa ish qidirishi kerakligini tushunadi. Future oxir-oqibat tugallanganda, voqea halqasi qaysi to'xtatilgan korutinni uyg'otishni biladi.
Shuning uchun siz har doim Future-ni `loop.create_future()` yordamida yaratishingiz kerak, bu erda `loop` joriy ishlayotgan voqea halqasidir. Turli voqea halqalari bo'ylab (yoki to'g'ri sinxronlashsiz turli mavzularda) Futures-ni yaratish va ulardan foydalanishga urinish xatolarga va oldindan aytib bo'lmaydigan xatti-harakatlarga olib keladi.
`await` aslida nima qiladi
Python tarjimoni result = await my_future
-ga duch kelganda, u kaput ostida bir nechta qadamlarni bajaradi:
- U `my_future.__await__()`-ni chaqiradi, bu iteratorni qaytaradi.
- U future allaqachon bajarilganmi yoki yo'qligini tekshiradi. Agar shunday bo'lsa, u natijani oladi (yoki istisnoni keltirib chiqaradi) va to'xtatmasdan davom etadi.
- Agar future kutilayotgan bo'lsa, u voqea halqasiga aytadi: "Bajarilishimni to'xtating va iltimos, ushbu aniq future tugallanganda meni uyg'oting."
- Keyin voqea halqasi o'z zimmasiga oladi va boshqa tayyor vazifalarni bajaradi.
- `My_future.set_result()` yoki `my_future.set_exception()` chaqirilgandan so'ng, voqea halqasi Future-ni bajarilgan deb belgilaydi va to'xtatilgan korutinni halqaning keyingi iteratsiyasida davom ettirishga rejalashtiradi.
Keng tarqalgan tuzoq: Futures-ni vazifalar bilan chalkashtirish
Keng tarqalgan xato - bu Task to'g'ri vosita bo'lganda, korutinning bajarilishini Future bilan qo'lda boshqarishga urinish.
Noto'g'ri yo'l (ortiqcha murakkab):
# Bu ko'p so'zli va keraksiz
async def main_wrong():
loop = asyncio.get_running_loop()
future = loop.create_future()
# Bizning maqsadimizni ishga tushirish va future-ni o'rnatish uchun alohida korutin
async def runner():
try:
result = await some_other_coro()
future.set_result(result)
except Exception as e:
future.set_exception(e)
# Biz ushbu runner korutinini qo'lda rejalashtirishimiz kerak
asyncio.create_task(runner())
# Nihoyat, biz future-ni kutishimiz mumkin
final_result = await future
To'g'ri yo'l (Task-dan foydalanish):
# Task bularning barchasini siz uchun qiladi!
async def main_right():
# Task - bu korutinni avtomatik ravishda boshqaradigan Future
task = asyncio.create_task(some_other_coro())
# Biz vazifani to'g'ridan-to'g'ri kutishimiz mumkin
final_result = await task
Task
Future
-ning kichik sinfi bo'lganligi sababli, ikkinchi misol nafaqat toza, balki funktsional jihatdan ekvivalent va samaraliroqdir.
Xulosa: Asyncio asosi
Asyncio Future - Python asinxron ekotizimining e'tirof etilmagan qahramoni. Bu async/await
-ning yuqori darajadagi sehrini mumkin qiladigan past darajadagi ibtidodir. Sizning kundalik kodingiz asosan korutinlarni yozishni va ularni Tasks sifatida rejalashtirishni o'z ichiga olsa-da, Futures-ni tushunish sizga hamma narsa qanday bog'langanligi haqida chuqur tushuncha beradi.
Futures-ni o'zlashtirish orqali siz quyidagi imkoniyatlarga ega bo'lasiz:
- Ishonch bilan disk raskadrovka qiling:
CancelledError
-ni ko'rganingizda yoki hech qachon qaytmaydigan korutinni ko'rganingizda, siz asosiy Future yoki Task holatini tushunasiz. - Har qanday kodni integratsiya qiling: Endi siz har qanday callback-ga asoslangan API-ni o'rash va uni zamonaviy asinxron dunyoda birinchi darajali fuqaroga aylantirish imkoniyatiga egasiz.
- Murakkab vositalarni yarating: Futures haqidagi bilim o'zingizning ilg'or konkurent va parallel dasturlash konstruktsiyalarini yaratishga birinchi qadamdir.
Shunday qilib, keyingi safar asyncio.create_task()
-dan foydalanganda yoki await asyncio.gather()
-dan foydalanganda, sahna ortida tinimsiz ishlayotgan kamtarona Future-ni qadrlashga bir oz vaqt ajrating. Bu mustahkam, kengaytiriladigan va oqlangan asinxron Python ilovalari qurilgan mustahkam poydevordir.