18-bo‘lim

Asinxron dasturlash

async va await, Task va Task<T>, bir nechta vazifani birga bajarish, ko'p uchraydigan xatolar va CancellationToken.

🕑 12 daqiqa o‘qish 📄 737 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Muammo
  2. Birinchi asinxron metod
  3. Bir nechta vazifani birga bajarish
  4. Natija qaytarish
  5. Birinchi tugaganini olish
  6. Istisnolar
  7. Ko'p uchraydigan xatolar
  8. CancellationToken - bekor qilish
  9. Qachon async kerak
  10. Xulosa

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 #

CSHARP
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";
}
Natija
1. Boshlandi
2. So'rov yuborildi, javob kutilmoqda
3. Olindi: ma'lumot
Kalit so'zMa'nosi
asyncBu metod ichida await ishlatiladi
awaitNatija kelguncha kut, lekin oqimni bloklama
TaskKelajakda tugaydigan ish (natijasiz)
Task<T>Kelajakda T qaytaradigan ish
async o'zi hech narsani tezlashtirmaydi

Bu 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:

  1. Serverda - bitta oqim kutayotganda boshqa so'rovlarga xizmat qila oladi, ya'ni server ko'proq foydalanuvchi ko'taradi;
  2. Ish stoli dasturida - interfeys qotib qolmaydi.

Tezlik esa keyingi bo'limdagi usul - vazifalarni birga boshlashdan keladi.

Bir nechta vazifani birga bajarish #

CSHARP
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);
}
Natija
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.

Bu yerda aniq vaqt chop etilmadi

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 #

CSHARP
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;
}
Natija
Natijalar: 4, 25, 100
Yig'indi: 129

Task.WhenAll natijalarni berilgan tartibda qaytaradi - qaysi biri oldin tugashidan qat'i nazar.

Birinchi tugaganini olish #

CSHARP
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;
}
Natija
Birinchi javob: tez
Sekin tugadimi: False

Istisnolar #

CSHARP
try
{
    await Xatoli();
}
catch (InvalidOperationException x)
{
    Console.WriteLine($"Ushlandi: {x.Message}");
}

async Task Xatoli()
{
    await Task.Delay(10);
    throw new InvalidOperationException("asinxron xato");
}
Natija
Ushlandi: asinxron xato

Asinxron metoddagi istisno oddiy try/catch bilan ushlanadi - await uni qaytadan tashlaydi.

WhenAll da bir nechta xato bo'lsa

Task.WhenAll da bir nechta vazifa xato bersa, await faqat birinchisini tashlaydi. Qolganlarini ko'rish uchun Task obyektining o'zidan olish kerak:

CSHARP
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);
}
Natija
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
CSHARP
// 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 yozmang
CSHARP
async 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.

Siklda await - sekin
CSHARP
using 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);
Natija
Ketma-ket sezilarli sekinroqmi: True

Agar vazifalar bir-biriga bog'liq bo'lmasa, Select + WhenAll ishlating.

CancellationToken - bekor qilish #

CSHARP
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");
}
Natija
Vazifa bekor qilindi
Holati: Canceled

Foydalanuvchi "bekor qilish" tugmasini bosganda yoki so'rov muddati tugaganda aynan shu vosita ishlatiladi.

Qachon async kerak #

Amalasync kerakmi
Fayl o'qish va yozishHa
Tarmoq so'rovi, HTTPHa
Ma'lumotlar bazasiHa
Matematik hisobYo'q - protsessor band, kutish yo'q
Ro'yxatni saralashYo'q
Kichik fayl (bir necha kilobayt)Farqi sezilmaydi
Nomlash an'anasi

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.

Amaliy topshiriq
  1. async Task qaytaruvchi oddiy metod yozing va await qiling.
  2. Task.Delay bilan kechikish yarating.
  3. Uchta vazifani ketma-ket va WhenAll bilan bajarib solishtiring.
  4. Task.WhenAll dan natijalar massivini oling.
  5. Task.WhenAny bilan birinchi tugaganini toping.
  6. Asinxron metoddagi istisnoni try/catch bilan ushlang.
  7. .Result nega xavfli ekanini o'z so'zingiz bilan yozing.
  8. async void va async Task farqini tushuntiring.
  9. Siklda await ni Select + WhenAll ga almashtiring.
  10. CancellationTokenSource bilan vazifani bekor qiling.

Xulosa #

  • async/await kodni tezlashtirmaydi - u kutish paytida oqimni bo'shatadi.
  • Tezlik Task.WhenAll bilan vazifalarni birga boshlashdan keladi.
  • Task - natijasiz, Task<T> - natija qaytaradigan ish.
  • WhenAll natijalarni berilgan tartibda qaytaradi.
  • Asinxron istisno oddiy try/catch bilan ushlanadi.
  • .Result va .Wait() ishlatmang - o'lik qulfga olib kelishi mumkin.
  • async void yozmang (hodisa ishlovchisidan tashqari).
  • Siklda await o'rniga Select + WhenAll.
  • Vaqtni o'lchaydigan sinovda aniq qiymatni emas, shartni tekshiring.

Keyingi bo'limda kodni sinovdan o'tkazishni o'rganamiz.

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.