19. Asinxron dasturlash¶
Zamonaviy dasturlarda bir vaqtning o‘zida ko‘plab ishlarni bajarish talab qilinadi.
Masalan, Softromeda dasturi bir vaqtning o‘zida:
API so‘rov yuborishi
fayl o‘qishi
ma’lumotlar bazasi bilan ishlashi
foydalanuvchidan ma’lumot kutishi
bir nechta server bilan aloqa qilishi
mumkin.
Agar dastur har bir ishni ketma-ket kutib bajarsa, vaqt behuda sarflanishi mumkin.
Bunday vaziyatlarda asinxron dasturlash juda foydali.
Bu bobda:
- sinxron va asinxron dasturlash
asyncawaitasyncio- coroutine
- task
- parallelizm va concurrency
- asinxron API bilan ishlash
mavzularini o‘rganamiz.
Sinxron dasturlash¶
Sinxron dasturlashda amallar odatda ketma-ket bajariladi.
Masalan:
import time
def birinchi_vazifa():
print(
"Birinchi vazifa boshlandi."
)
time.sleep(2)
print(
"Birinchi vazifa tugadi."
)
def ikkinchi_vazifa():
print(
"Ikkinchi vazifa boshlandi."
)
time.sleep(2)
print(
"Ikkinchi vazifa tugadi."
)
birinchi_vazifa()
ikkinchi_vazifa()
Bu dasturda:
birinchi vazifa
↓
kutish
↓
birinchi vazifa tugaydi
↓
ikkinchi vazifa
↓
kutish
↓
ikkinchi vazifa tugaydi
Agar har bir vazifa 2 soniya kutsa, umumiy vaqt taxminan:
4 soniya
bo‘ladi.
Asinxron dasturlash¶
Asinxron dasturlashda dastur kutish vaqtida boshqa ishni bajarishi mumkin.
Masalan:
Vazifa A → kutmoqda
↓
Vazifa B → bajarilmoqda
↓
Vazifa A → davom etmoqda
Bu ayniqsa:
tarmoq
API
fayl
ma’lumotlar bazasi
foydalanuvchi kiritishi
kabi kutish ko‘p bo‘ladigan operatsiyalarda foydali.
async¶
Python'da asinxron funksiya yaratish uchun:
async def
ishlatiladi.
Masalan:
async def salom_berish():
print(
"Salom, Softromeda!"
)
Bu oddiy funksiya emas.
Bu coroutine function hisoblanadi.
Coroutine¶
async def yordamida yaratilgan funksiya chaqirilganda coroutine obyektini qaytaradi.
Masalan:
async def salom_berish():
print(
"Salom, Softromeda!"
)
natija = salom_berish()
print(
natija
)
Bu kod funksiyani oddiy def kabi darhol bajarmaydi.
Coroutine'ni bajarish uchun asinxron muhit kerak.
await¶
Coroutine ichidagi boshqa asinxron operatsiyani kutish uchun:
await
ishlatiladi.
Masalan:
import asyncio
async def kutish():
print(
"Kutish boshlandi."
)
await asyncio.sleep(2)
print(
"Kutish tugadi."
)
Bu yerda:
await asyncio.sleep(2)
dasturga:
"2 soniya kut, lekin bu vaqt davomida event loop boshqa coroutine'larni ishlatishi mumkin"
degan imkoniyatni beradi.
asyncio¶
Python standart kutubxonasida asinxron dasturlash uchun:
asyncio
moduli mavjud.
Uni:
import asyncio
orqali ishlatamiz.
asyncio.run()¶
Asinxron dasturni ishga tushirishning qulay usullaridan biri:
asyncio.run()
Masalan:
import asyncio
async def asosiy():
print(
"Softromeda dasturi ishga tushdi."
)
asyncio.run(
asosiy()
)
Bu yerda:
asosiy()
coroutine yaratadi.
asyncio.run() esa uni bajaradi.
Asinxron kutish¶
import asyncio
async def vazifa():
print(
"Vazifa boshlandi."
)
await asyncio.sleep(2)
print(
"Vazifa tugadi."
)
asyncio.run(
vazifa()
)
Bu yerda:
await asyncio.sleep(2)
asinxron kutish hisoblanadi.
time.sleep() va asyncio.sleep()¶
Bu ikkisini farqlash juda muhim.
Sinxron:
time.sleep(2)
Asinxron:
await asyncio.sleep(2)
time.sleep() event loop'ni bloklaydi.
asyncio.sleep() esa boshqa coroutine'larning ishlashiga imkon beradi.
Shuning uchun asinxron dasturda odatda:
await asyncio.sleep(...)
ishlatiladi.
Bir nechta coroutine¶
Endi ikkita coroutine yaratamiz:
import asyncio
async def birinchi_vazifa():
print(
"Birinchi vazifa boshlandi."
)
await asyncio.sleep(2)
print(
"Birinchi vazifa tugadi."
)
async def ikkinchi_vazifa():
print(
"Ikkinchi vazifa boshlandi."
)
await asyncio.sleep(2)
print(
"Ikkinchi vazifa tugadi."
)
async def asosiy():
await birinchi_vazifa()
await ikkinchi_vazifa()
asyncio.run(
asosiy()
)
Bu kod hali ham ketma-ket ishlaydi.
Chunki:
await birinchi_vazifa()
tugagandan keyin:
await ikkinchi_vazifa()
boshlanadi.
gather()¶
Bir nechta coroutine'ni birgalikda bajarish uchun:
asyncio.gather()
ishlatiladi.
import asyncio
async def birinchi_vazifa():
print(
"Birinchi vazifa boshlandi."
)
await asyncio.sleep(2)
print(
"Birinchi vazifa tugadi."
)
async def ikkinchi_vazifa():
print(
"Ikkinchi vazifa boshlandi."
)
await asyncio.sleep(2)
print(
"Ikkinchi vazifa tugadi."
)
async def asosiy():
await asyncio.gather(
birinchi_vazifa(),
ikkinchi_vazifa()
)
asyncio.run(
asosiy()
)
Bu holatda ikkala vazifaning kutish vaqti bir-birining ustiga tushishi mumkin.
Shuning uchun umumiy vaqt taxminan:
2 soniya
atrofida bo‘lishi mumkin.
Coroutine'larni parallel rejalashtirish¶
Bu yerda muhim tushuncha mavjud.
asyncio odatda haqiqiy CPU parallelizmini ta’minlamaydi.
U concurrencyni ta’minlaydi.
Ya’ni:
Vazifa A kutmoqda
↓
Vazifa B ishlaydi
↓
Vazifa B kutmoqda
↓
Vazifa A davom etadi
Bu CPU hisoblashlarini bir nechta yadroda bir vaqtning o‘zida bajarish degani emas.
Concurrency va parallelizm¶
Concurrency — bir nechta vazifaning bajarilishini muvofiqlashtirish.
Masalan:
A kutmoqda
B ishlamoqda
B kutmoqda
C ishlamoqda
A davom etmoqda
Parallelizm esa bir nechta hisoblashning haqiqatan bir vaqtning o‘zida bajarilishi.
Masalan:
Yadro 1 → A vazifa
Yadro 2 → B vazifa
asyncio asosan:
concurrency
uchun ishlatiladi.
create_task()¶
Coroutine'ni task sifatida rejalashtirish uchun:
asyncio.create_task()
ishlatiladi.
Masalan:
import asyncio
async def xabar_yuborish():
print(
"Xabar yuborilmoqda."
)
await asyncio.sleep(2)
print(
"Xabar yuborildi."
)
async def ma'lumot_yuklash():
print(
"Ma’lumot yuklanmoqda."
)
await asyncio.sleep(3)
print(
"Ma’lumot yuklandi."
)
async def asosiy():
xabar_vazifasi = asyncio.create_task(
xabar_yuborish()
)
ma'lumot_vazifasi = asyncio.create_task(
ma'lumot_yuklash()
)
await xabar_vazifasi
await ma'lumot_vazifasi
asyncio.run(
asosiy()
)
Bu yerda ikkala vazifa oldindan rejalashtiriladi.
Task nima?¶
Task — coroutine'ni event loop tomonidan bajarish uchun rejalashtirilgan obyekt.
Masalan:
vazifa = asyncio.create_task(
ma'lumot_yuklash()
)
Bu coroutine'ni task sifatida rejalashtiradi.
Keyinchalik:
await vazifa
orqali uning tugashini kutish mumkin.
Task natijasini olish¶
Coroutine qiymat qaytarishi mumkin.
import asyncio
async def hisoblash():
await asyncio.sleep(1)
return 100
async def asosiy():
vazifa = asyncio.create_task(
hisoblash()
)
natija = await vazifa
print(
f"Natija: {natija}"
)
asyncio.run(
asosiy()
)
Natija:
Natija: 100
gather() natijalarni qaytarishi¶
Bir nechta coroutine natijasini olish mumkin:
import asyncio
async def birinchi():
await asyncio.sleep(1)
return "Birinchi"
async def ikkinchi():
await asyncio.sleep(2)
return "Ikkinchi"
async def asosiy():
natijalar = await asyncio.gather(
birinchi(),
ikkinchi()
)
print(
natijalar
)
asyncio.run(
asosiy()
)
Natija:
['Birinchi', 'Ikkinchi']
Asinxron funksiya argumentlari¶
Asinxron funksiyalar oddiy funksiyalar kabi argument qabul qiladi.
import asyncio
async def foydalanuvchini_olish(
foydalanuvchi_id
):
await asyncio.sleep(1)
return {
"id": foydalanuvchi_id,
"ism": "Ali"
}
async def asosiy():
foydalanuvchi = await (
foydalanuvchini_olish(1)
)
print(
foydalanuvchi
)
asyncio.run(
asosiy()
)
Asinxron API bilan ishlash¶
Oldingi bobda HTTP API bilan ishlashni o‘rgandik.
Oddiy requests kutubxonasi sinxron ishlaydi.
Masalan:
import requests
javob = requests.get(
"https://example.com/api"
)
Agar ko‘plab API so‘rovlarini bajarish kerak bo‘lsa, asinxron HTTP mijozdan foydalanish foydali bo‘lishi mumkin.
Mashhur kutubxonalardan biri:
aiohttp
aiohttp¶
O‘rnatish:
pip install aiohttp
Keyin:
import aiohttp
deb ishlatish mumkin.
Asinxron GET so‘rovi¶
import asyncio
import aiohttp
async def ma'lumot_olish():
manzil = (
"https://example.com/api"
)
async with aiohttp.ClientSession() as sessiya:
async with sessiya.get(
manzil
) as javob:
ma'lumot = await javob.json()
print(
ma'lumot
)
asyncio.run(
ma'lumot_olish()
)
Bu yerda:
await javob.json()
JSON ma’lumotini asinxron tarzda olishga imkon beradi.
Bir nechta API so‘rovi¶
Asinxron dasturlashning kuchli tomonlaridan biri — bir nechta tarmoq so‘rovlarini birgalikda bajarish.
import asyncio
import aiohttp
async def ma'lumot_olish(
sessiya,
manzil
):
async with sessiya.get(
manzil
) as javob:
return await javob.json()
async def asosiy():
manzillar = [
"https://example.com/api/birinchi",
"https://example.com/api/ikkinchi",
"https://example.com/api/uchinchi"
]
async with aiohttp.ClientSession() as sessiya:
vazifalar = [
ma'lumot_olish(
sessiya,
manzil
)
for manzil in manzillar
]
natijalar = await asyncio.gather(
*vazifalar
)
for natija in natijalar:
print(
natija
)
asyncio.run(
asosiy()
)
Bu yerda bir nechta API so‘rovi birgalikda rejalashtiriladi.
async with¶
Asinxron resurslarni boshqarishda:
async with
ishlatiladi.
Masalan:
async with aiohttp.ClientSession() as sessiya:
...
Bu sessiyani to‘g‘ri ochish va yopishga yordam beradi.
Asinxron kontekst menejeri¶
Oddiy Python'da:
with
ishlatiladi.
Asinxron kodda:
async with
ishlatilishi mumkin.
Masalan:
async with sessiya.get(
manzil
) as javob:
ma'lumot = await javob.json()
Asinxron iteratorlar¶
Asinxron dasturlashda asinxron iteratorlardan ham foydalanish mumkin.
Buning uchun:
async for
ishlatiladi.
Masalan:
import asyncio
async def ma'lumotlar():
for son in range(5):
await asyncio.sleep(1)
yield son
async def asosiy():
async for son in ma'lumotlar():
print(
f"Son: {son}"
)
asyncio.run(
asosiy()
)
Bu yerda ma’lumotlar bosqichma-bosqich olinadi.
Asinxron generator¶
async def va yield birgalikda ishlatilsa, asinxron generator hosil bo‘ladi.
async def sonlarni_yuborish():
for son in range(5):
await asyncio.sleep(1)
yield son
Uni:
async for
bilan ishlatamiz.
asyncio.wait()¶
Task'larni kutish uchun:
asyncio.wait()
ham ishlatilishi mumkin.
import asyncio
async def birinchi():
await asyncio.sleep(1)
print(
"Birinchi tugadi."
)
async def ikkinchi():
await asyncio.sleep(2)
print(
"Ikkinchi tugadi."
)
async def asosiy():
vazifalar = {
asyncio.create_task(
birinchi()
),
asyncio.create_task(
ikkinchi()
)
}
await asyncio.wait(
vazifalar
)
asyncio.run(
asosiy()
)
Vazifani bekor qilish¶
Task'ni bekor qilish mumkin:
vazifa.cancel()
Masalan:
import asyncio
async def uzoq_vazifa():
print(
"Uzoq vazifa boshlandi."
)
try:
await asyncio.sleep(10)
except asyncio.CancelledError:
print(
"Uzoq vazifa bekor qilindi."
)
raise
async def asosiy():
vazifa = asyncio.create_task(
uzoq_vazifa()
)
await asyncio.sleep(2)
vazifa.cancel()
try:
await vazifa
except asyncio.CancelledError:
print(
"Vazifa bekor qilindi."
)
asyncio.run(
asosiy()
)
CancelledError¶
Bekor qilingan task:
asyncio.CancelledError
istisnosini keltirib chiqarishi mumkin.
Shuning uchun resurslarni tozalash yoki maxsus ish bajarish kerak bo‘lsa:
try:
...
except asyncio.CancelledError:
...
raise
ko‘rinishidan foydalanish mumkin.
Timeout¶
Asinxron operatsiya juda uzoq davom etmasligi uchun timeout qo‘yish mumkin.
import asyncio
async def uzoq_vazifa():
await asyncio.sleep(10)
return "Tayyor"
async def asosiy():
try:
natija = await asyncio.wait_for(
uzoq_vazifa(),
timeout=2
)
print(
natija
)
except asyncio.TimeoutError:
print(
"Vazifa uchun vaqt tugadi."
)
asyncio.run(
asosiy()
)
Bu yerda vazifa 10 soniya davom etmoqchi, lekin:
2 soniyalik timeout
berilgan.
Semaphore¶
Bir vaqtning o‘zida juda ko‘p API so‘rovi yuborish serverga ortiqcha yuk berishi mumkin.
Bunday vaziyatda:
asyncio.Semaphore
ishlatiladi.
Masalan, bir vaqtning o‘zida faqat 3 ta vazifaga ruxsat berish:
import asyncio
cheklov = asyncio.Semaphore(3)
async def vazifa(raqam):
async with cheklov:
print(
f"{raqam}-vazifa boshlandi."
)
await asyncio.sleep(2)
print(
f"{raqam}-vazifa tugadi."
)
async def asosiy():
vazifalar = [
vazifa(raqam)
for raqam in range(10)
]
await asyncio.gather(
*vazifalar
)
asyncio.run(
asosiy()
)
Bu usul ko‘plab tarmoq so‘rovlarini boshqarishda foydali.
Asinxron dasturlash qachon foydali?¶
Asinxron dasturlash ayniqsa quyidagi vaziyatlarda foydali:
HTTP so‘rovlari
API
websocket
tarmoq dasturlari
ko‘plab I/O operatsiyalari
server dasturlari
bir nechta tashqi xizmat bilan ishlash
Qachon asyncio ishlatmaslik kerak?¶
Agar dastur asosan og‘ir CPU hisoblashlaridan iborat bo‘lsa:
katta matematik hisoblashlar
rasmni og‘ir qayta ishlash
video kodlash
katta ma’lumotlarni hisoblash
asyncio har doim ham yaxshi yechim emas.
Chunki asyncioning asosiy kuchi:
I/O-bound
vazifalarda ko‘rinadi.
CPU-bound vazifalar uchun boshqa yondashuvlar, masalan:
multiprocessing
ProcessPoolExecutor
ko‘proq mos kelishi mumkin.
I/O-bound va CPU-bound¶
I/O-bound — dastur ko‘p vaqtni tashqi operatsiyani kutishga sarflaydi.
Masalan:
API
internet
fayl
ma’lumotlar bazasi
CPU-bound — dastur ko‘p vaqtni protsessorda hisoblashga sarflaydi.
Masalan:
katta matematik hisoblash
murakkab algoritm
video qayta ishlash
Asinxron dasturlash:
I/O-bound
vazifalarda ayniqsa foydali.
Amaliy loyiha — Softromeda asinxron API mijoz¶
Endi bir nechta API manzillariga asinxron ravishda so‘rov yuboradigan dastur yaratamiz.
import asyncio
import aiohttp
class SoftromedaAPI:
def __init__(
self,
asosiy_manzil
):
self.asosiy_manzil = (
asosiy_manzil.rstrip("/")
)
async def ma'lumot_olish(
self,
sessiya,
yo'l
):
manzil = (
f"{self.asosiy_manzil}{yo'l}"
)
try:
async with sessiya.get(
manzil,
timeout=aiohttp.ClientTimeout(
total=10
)
) as javob:
javob.raise_for_status()
return await javob.json()
except (
aiohttp.ClientError,
asyncio.TimeoutError
):
print(
f"So‘rov bajarilmadi: "
f"{manzil}"
)
return None
async def bir nechta_ma'lumot_olish(
self,
yo'llar
):
async with aiohttp.ClientSession() as sessiya:
vazifalar = [
self.ma'lumot_olish(
sessiya,
yo'l
)
for yo'l in yo'llar
]
natijalar = await asyncio.gather(
*vazifalar
)
return natijalar
async def asosiy():
api = SoftromedaAPI(
"https://example.com/api"
)
yo'llar = [
"/foydalanuvchilar",
"/mahsulotlar",
"/buyurtmalar"
]
natijalar = (
await api.bir nechta_ma'lumot_olish(
yo'llar
)
)
for indeks, natija in enumerate(
natijalar,
start=1
):
print(
f"{indeks}-natija:"
)
print(
natija
)
print(
"--------------------"
)
asyncio.run(
asosiy()
)
Bu loyiha quyidagi jarayonni ko‘rsatadi:
Softromeda API mijoz
↓
uchta API manzili
↓
uchta coroutine
↓
asyncio.gather()
↓
birgalikda bajarilish
↓
JSON natijalar
Asinxron kodni loyihalash¶
Katta dasturda quyidagi qatlamlarni ajratish yaxshi:
API qatlami
↓
ma’lumotlar qatlami
↓
biznes mantiq
↓
foydalanuvchi interfeysi
Masalan:
class SoftromedaAPI:
...
faqat API bilan ishlashi mumkin.
Boshqa funksiyalar esa:
foydalanuvchilarni_qayta_ishlash()
kabi ma’lumotlarni qayta ishlashi mumkin.
Asinxron dasturlashning asosiy tushunchalari¶
Bu bobdagi eng muhim tushunchalar:
async
await
coroutine
task
event loop
asyncio
gather
create_task
async with
async for
timeout
semaphore
concurrency
Ularning qisqacha vazifasi:
async
asinxron funksiya yaratadi
await
asinxron operatsiyani kutadi
coroutine
asinxron bajarilishi mumkin bo‘lgan obyekt
task
coroutine'ni bajarish uchun rejalashtirilgan obyekt
gather
bir nechta coroutine'ni birgalikda kutadi
create_task
coroutine'ni task sifatida rejalashtiradi
async with
asinxron kontekst menejeri
async for
asinxron iterator bilan ishlaydi
Amaliy mashqlar¶
Mashq 1¶
asyncio yordamida 3 ta asinxron funksiya yarating.
Har bir funksiya turli vaqt kutib, o‘z natijasini chiqarsin.
Mashq 2¶
Uchta coroutine'ni:
asyncio.gather()
yordamida birgalikda ishga tushiring.
Mashq 3¶
asyncio.create_task() yordamida ikkita task yarating.
Mashq 4¶
Task'ning qaytargan natijasini oling.
Mashq 5¶
asyncio.wait_for() yordamida timeout yarating.
Mashq 6¶
Uzoq davom etadigan task'ni cancel() yordamida bekor qiling.
Mashq 7¶
aiohttp yordamida API'ga asinxron GET so‘rovi yuboring.
Mashq 8¶
Bir vaqtning o‘zida 5 ta API so‘rovini bajaring.
Mashq 9¶
asyncio.Semaphore yordamida bir vaqtning o‘zida faqat 2 ta so‘rov bajarilishini ta’minlang.
Mashq 10¶
Softromeda uchun asinxron API mijoz yarating.
Dastur:
API bilan asinxron aloqa qilsin
bir nechta so‘rovni birgalikda bajarsin
timeout ishlatsin
xatolarni boshqarsin
tasklardan foydalansin
semaphore yordamida so‘rovlar sonini cheklasin
JSON ma’lumotlarini qayta ishlasin
Bob xulosasi¶
Bu bobda Python'da asinxron dasturlashni o‘rgandik.
Asinxron funksiya:
async def
orqali yaratiladi.
Asinxron operatsiyani kutish:
await
orqali bajariladi.
Asosiy modul:
import asyncio
Asosiy vositalar:
asyncio.run()
asyncio.gather()
asyncio.create_task()
asyncio.wait()
asyncio.wait_for()
asyncio.Semaphore
Tarmoq bilan asinxron ishlash uchun:
aiohttp
kabi kutubxonalardan foydalanish mumkin.
Eng muhim fikr:
asyncio
↓
concurrency
↓
I/O-bound vazifalar
↓
API va tarmoq operatsiyalari
Asinxron dasturlashni yaxshi tushunish Python'da zamonaviy serverlar, API mijozlari va tarmoq dasturlarini yaratishda juda muhim.
Keyingi bobda 20. Professional Python dasturlash va loyiha yaratish mavzusini o‘rganamiz.