18-bo‘lim
Asinxron dasturlash
async va await, Task va Task<T>, bir nechta vazifani birga bajarish, ko'p uchraydigan xatolar va CancellationToken.
Ushbu bo‘lim mundarijasi
Dastur ko'pincha kutadi: fayl o'qilishini, tarmoqdan javob kelishini, bazadan natija olishni. Kutayotgan paytda protsessor bo'sh turadi.
async va await shu bo'sh vaqtdan foydalanish imkonini beradi.
Muammo #
Uchta tarmoq so'rovi har biri bir soniya davom etsa, ketma-ket bajarilganda uch soniya ketadi. Aslida ularni bir vaqtda yuborish mumkin edi.
Birinchi asinxron metod #
Console.WriteLine("1. Boshlandi");
string natija = await MalumotOl();
Console.WriteLine($"3. Olindi: {natija}");
async Task<string> MalumotOl()
{
Console.WriteLine("2. So'rov yuborildi, javob kutilmoqda");
await Task.Delay(50);
return "ma'lumot";
}
1. Boshlandi
2. So'rov yuborildi, javob kutilmoqda
3. Olindi: ma'lumot
| Kalit so'z | Ma'nosi |
|---|---|
async | Bu metod ichida await ishlatiladi |
await | Natija kelguncha kut, lekin oqimni bloklama |
Task | Kelajakda tugaydigan ish (natijasiz) |
Task<T> | Kelajakda T qaytaradigan ish |
async o'zi hech narsani tezlashtirmaydiBu eng ko'p uchraydigan tushunmovchilik. async kodni parallel
bajarmaydi va tezroq qilmaydi.
U faqat bitta narsa qiladi: kutish paytida oqimni bo'shatadi.
Foydasi ikki joyda ko'rinadi:
- Serverda - bitta oqim kutayotganda boshqa so'rovlarga xizmat qila oladi, ya'ni server ko'proq foydalanuvchi ko'taradi;
- Ish stoli dasturida - interfeys qotib qolmaydi.
Tezlik esa keyingi bo'limdagi usul - vazifalarni birga boshlashdan keladi.
Bir nechta vazifani birga bajarish #
using System.Diagnostics;
var soat = Stopwatch.StartNew();
// ketma-ket
await Ish("A", 100);
await Ish("B", 100);
await Ish("C", 100);
long ketmaKet = soat.ElapsedMilliseconds;
soat.Restart();
// birga
await Task.WhenAll(Ish("D", 100), Ish("E", 100), Ish("F", 100));
long birga = soat.ElapsedMilliseconds;
Console.WriteLine($"Ketma-ket 250 ms dan ko'pmi: {ketmaKet > 250}");
Console.WriteLine($"Birga 200 ms dan kammi: {birga < 200}");
async Task Ish(string nom, int ms)
{
await Task.Delay(ms);
}
Ketma-ket 250 ms dan ko'pmi: True
Birga 200 ms dan kammi: True
Uchala vazifa birga boshlangani uchun umumiy vaqt uch barobar emas, deyarli bittasining vaqtiga teng bo'ldi.
Diqqat qiling: chiqishda 312 ms kabi aniq son yo'q, faqat
chegaradan o'tdimi degan javob bor.
Sababi: aniq vaqt kompyuterdan kompyuterga, hatto bitta mashinada
har safar o'zgaradi. Agar darslikda 312 ms deb yozilsa, siz boshqa
natija olasiz va bu chalkashlik tug'diradi.
Bu vaqtni o'lchaydigan har qanday sinovga tegishli qoida: aniq qiymatni emas, shartni tekshiring.
Natija qaytarish #
int[] natijalar = await Task.WhenAll(
Hisobla(2),
Hisobla(5),
Hisobla(10));
Console.WriteLine($"Natijalar: {string.Join(", ", natijalar)}");
Console.WriteLine($"Yig'indi: {natijalar.Sum()}");
async Task<int> Hisobla(int son)
{
await Task.Delay(10);
return son * son;
}
Natijalar: 4, 25, 100
Yig'indi: 129
Task.WhenAll natijalarni berilgan tartibda qaytaradi - qaysi biri
oldin tugashidan qat'i nazar.
Birinchi tugaganini olish #
Task<string> tez = Javob("tez", 10);
Task<string> sekin = Javob("sekin", 200);
Task<string> birinchi = await Task.WhenAny(tez, sekin);
Console.WriteLine($"Birinchi javob: {await birinchi}");
Console.WriteLine($"Sekin tugadimi: {sekin.IsCompleted}");
async Task<string> Javob(string nom, int ms)
{
await Task.Delay(ms);
return nom;
}
Birinchi javob: tez
Sekin tugadimi: False
Istisnolar #
try
{
await Xatoli();
}
catch (InvalidOperationException x)
{
Console.WriteLine($"Ushlandi: {x.Message}");
}
async Task Xatoli()
{
await Task.Delay(10);
throw new InvalidOperationException("asinxron xato");
}
Ushlandi: asinxron xato
Asinxron metoddagi istisno oddiy try/catch bilan ushlanadi - await
uni qaytadan tashlaydi.
WhenAll da bir nechta xato bo'lsaTask.WhenAll da bir nechta vazifa xato bersa, await faqat
birinchisini tashlaydi. Qolganlarini ko'rish uchun Task obyektining
o'zidan olish kerak:
Task hammasi = Task.WhenAll(Xato("birinchi"), Xato("ikkinchi"));
try
{
await hammasi;
}
catch (Exception)
{
var xatolar = hammasi.Exception!.InnerExceptions;
Console.WriteLine($"Jami xatolar: {xatolar.Count}");
// tartib kafolatlanmagan, shuning uchun saralaymiz
foreach (var x in xatolar.OrderBy(x => x.Message))
{
Console.WriteLine($" - {x.Message}");
}
}
async Task Xato(string nom)
{
await Task.Delay(10);
throw new InvalidOperationException(nom);
}
Jami xatolar: 2
- birinchi
- ikkinchi
Kodda OrderBy nega qo'shilgani muhim: InnerExceptions tartibi
kafolatlanmaydi. U qaysi vazifa avval tugashiga bog'liq va har
ishga tushirishda o'zgarishi mumkin.
Bu asinxron kod bilan ishlashdagi umumiy qoida: bir vaqtda bajarilgan vazifalar natijasining kelish tartibiga tayanmang.
Ko'p uchraydigan xatolar #
.Result va .Wait() ishlatmang// YOMON - oqimni bloklaydi
string a = MalumotOl().Result;
MalumotOl().Wait();
// YAXSHI
string b = await MalumotOl();
.Result asinxron metodni sinxron kutishga majbur qiladi. Bu
async ning butun ma'nosini yo'qqa chiqaradi.
Bundan ham yomoni: ba'zi muhitlarda (eski ASP.NET, ish stoli dasturlari) bu o'lik qulf ga olib keladi - dastur butunlay qotib qoladi va hech qanday xato bermaydi.
Qoida: async ni yuqorigacha olib boring. Metod async bo'lsa,
uni chaqirgan metod ham async bo'lsin.
async void yozmangasync void Yomon() { } // istisnoni ushlab bo'lmaydi
async Task Yaxshi() { } // to'g'ri
async void metodidagi istisno hech qayerda ushlanmaydi va butun
dasturni qulatadi.
Yagona istisno - hodisa ishlovchilari (button.Click += ...), chunki
ularning imzosi void bo'lishi shart. Qolgan hamma joyda Task
qaytaring.
await - sekinusing System.Diagnostics;
int[] sonlar = [1, 2, 3, 4, 5];
var soat = Stopwatch.StartNew();
// sekin: har biri navbat bilan
foreach (int son in sonlar)
{
await Ishla(son);
}
long ketmaKet = soat.ElapsedMilliseconds;
soat.Restart();
// tez: hammasi birga
await Task.WhenAll(sonlar.Select(Ishla));
long birga = soat.ElapsedMilliseconds;
Console.WriteLine(
$"Ketma-ket sezilarli sekinroqmi: {ketmaKet > birga * 2}");
async Task Ishla(int son) => await Task.Delay(50);
Ketma-ket sezilarli sekinroqmi: True
Agar vazifalar bir-biriga bog'liq bo'lmasa, Select + WhenAll
ishlating.
CancellationToken - bekor qilish #
using var manba = new CancellationTokenSource();
Task vazifa = UzoqIsh(manba.Token);
manba.Cancel();
try
{
await vazifa;
}
catch (OperationCanceledException)
{
Console.WriteLine("Vazifa bekor qilindi");
}
Console.WriteLine($"Holati: {vazifa.Status}");
async Task UzoqIsh(CancellationToken token)
{
await Task.Delay(5000, token);
Console.WriteLine("Bu hech qachon chiqmaydi");
}
Vazifa bekor qilindi
Holati: Canceled
Foydalanuvchi "bekor qilish" tugmasini bosganda yoki so'rov muddati tugaganda aynan shu vosita ishlatiladi.
Qachon async kerak #
| Amal | async kerakmi |
|---|---|
| Fayl o'qish va yozish | Ha |
| Tarmoq so'rovi, HTTP | Ha |
| Ma'lumotlar bazasi | Ha |
| Matematik hisob | Yo'q - protsessor band, kutish yo'q |
| Ro'yxatni saralash | Yo'q |
| Kichik fayl (bir necha kilobayt) | Farqi sezilmaydi |
Asinxron metod nomi Async bilan tugaydi: MalumotOlAsync,
SaqlashAsync.
Bu majburiy emas, lekin .NET kutubxonalarida qat'iy qo'llaniladi va
kod o'qiyotgan odam await yozish kerakligini darhol biladi.
Bu darslikdagi misollarda qisqalik uchun bu qoida qo'llanilmadi.
async Taskqaytaruvchi oddiy metod yozing vaawaitqiling.Task.Delaybilan kechikish yarating.- Uchta vazifani ketma-ket va
WhenAllbilan bajarib solishtiring. Task.WhenAlldan natijalar massivini oling.Task.WhenAnybilan birinchi tugaganini toping.- Asinxron metoddagi istisnoni
try/catchbilan ushlang. .Resultnega xavfli ekanini o'z so'zingiz bilan yozing.async voidvaasync Taskfarqini tushuntiring.- Siklda
awaitniSelect+WhenAllga almashtiring. CancellationTokenSourcebilan vazifani bekor qiling.
Xulosa #
async/awaitkodni tezlashtirmaydi - u kutish paytida oqimni bo'shatadi.- Tezlik
Task.WhenAllbilan vazifalarni birga boshlashdan keladi. Task- natijasiz,Task<T>- natija qaytaradigan ish.WhenAllnatijalarni berilgan tartibda qaytaradi.- Asinxron istisno oddiy
try/catchbilan ushlanadi. .Resultva.Wait()ishlatmang - o'lik qulfga olib kelishi mumkin.async voidyozmang (hodisa ishlovchisidan tashqari).- Siklda
awaito'rnigaSelect+WhenAll. - Vaqtni o'lchaydigan sinovda aniq qiymatni emas, shartni tekshiring.
Keyingi bo'limda kodni sinovdan o'tkazishni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.