Internet Windows Android
Kengaytirish

Asinxron dasturlash. Asinxron delegatlar Asinxron dasturlash kodiga misollar c Buyurtma

Dasturlashda asinxroniya

Ivan Borisov

An'anaviy dasturlashda sinxron dasturlash qo'llaniladi - ko'rsatmalarning sinxron tizim chaqiruvlari bilan ketma-ket bajarilishi, tizim operatsiyasi, masalan, diskdan o'qish tugaguniga qadar bajarish oqimini butunlay blokirovka qiladi. Misol tariqasida echo server quyida yozilgan:

While (true) (std::string data; auto socket = Socket(localhost, port); socket.wait_connection(); while (!socket.end_of_connection()) (ma'lumotlar = socket.read(); // Bloklash rozetka. yozish (ma'lumotlar); // Qulflash ) )

Read() va write() usullari chaqirilganda, tarmoq kiritish/chiqarishni kutish vaqtida joriy bajarilish oqimi uzilib qoladi. Bundan tashqari, ko'pincha dastur shunchaki kutadi. Yuqori yuklangan tizimlarda bu ko'pincha sodir bo'ladi - deyarli har doim dastur nimanidir kutadi: disk, DBMS, tarmoq, UI, umuman, dasturning o'zidan mustaqil bo'lgan ba'zi tashqi hodisa. Yengil yuklangan tizimlarda buni har bir blokirovka harakati uchun yangi ish zarrachasini yaratish orqali hal qilish mumkin. Bir ip uxlab yotgan bo'lsa, ikkinchisi ishlaydi.

Lekin foydalanuvchilar ko'p bo'lsa nima qilish kerak? Agar siz har biri uchun kamida bitta ip yaratsangiz, bunday serverning ishlashi ipning bajarilishi konteksti doimiy ravishda o'zgarib turishi tufayli keskin pasayadi. Bundan tashqari, har bir ish zarrachasi o'z bajarish kontekstiga ega, jumladan, stek xotirasi, minimal hajmi 4 KB. Asinxron dasturlash bu muammoni hal qilishi mumkin.

Asinxroniya

Dasturlashda asinxroniya - bu jarayonning bloklanmaydigan tizim chaqiruvi rejimida bajarilishi, bu dastur ipini qayta ishlashni davom ettirish imkonini beradi. Asinxron dasturlashni amalga oshirishning bir necha usullari mavjud, ular haqida quyida bilib olasiz.

Qayta qo'ng'iroqlar

Asinxron dasturni yozish uchun siz qayta qo'ng'iroq qilish funktsiyalaridan (inglizcha qayta qo'ng'iroq - qayta qo'ng'iroqdan) foydalanishingiz mumkin - vazifa bajarilgandan so'ng ba'zi bir hodisa ishlov beruvchisi tomonidan asinxron ravishda chaqiriladigan funktsiyalar. Qayta qo'ng'iroq qilish funksiyalaridan foydalanadigan serverning qayta yozilgan namunasi:

Holbuki (true) (avtomatik rozetka = Socket(localhost, port); socket.wait_connection(); // Hali bloklangan socket.async_read((auto &data) /* * Tarmoq bloklanmagan, lambda funksiyasi chaqiriladi * har safar rozetkadan yangi ma'lumotlarni olgandan so'ng, * va asosiy ip yangi rozetka yaratish uchun ketadi va * yangi ulanishni kuting. */ ( socket.async_write(data, (auto &socket)) ( if (socket.end_of_connection) ()) rozetka.yopish(); ) ); )); )

wait_connection() da biz hali ham biror narsani kutyapmiz, lekin hozir wait_connection() funksiyasi ichida OS rejalashtiruvchisi kabi biror narsa amalga oshirilishi mumkin, lekin qayta qo'ng'iroq qilish funksiyalari bilan (biz yangi ulanishni kutayotganimizda, nega eskilarini qayta ishlamasligimiz kerak) Masalan, navbat orqali). Agar rozetkada yangi ma'lumotlar paydo bo'lsa - async_read() da lambda yoki ma'lumotlar async_write() da yozilgan bo'lsa, qayta qo'ng'iroq qilish funksiyasi chaqiriladi.

Natijada, biz bitta ipda bir nechta ulanishlarning asinxron ishlashiga erishdik, bu esa kamroq kutadi. Ushbu asinxroniyani protsessor vaqtidan foydalanishdan to'liq foyda olish uchun parallellashtirish ham mumkin.

Ushbu yondashuv bilan bir qator muammolar mavjud. Birinchisi, hazil bilan qo'ng'iroqni jahannam deb atashadi. Bu mavzuni o'qib bo'lmaydigan va xunuk ekanligini tushunish uchun ushbu mavzu bo'yicha google rasmlarini qidirish kifoya. Bizning misolimizda faqat ikkita ichki qayta qo'ng'iroq funksiyasi mavjud, ammo yana ko'p bo'lishi mumkin.

Ikkinchi muammo shundaki, kod endi sinxron ko'rinmaydi: wait_connection() dan lambdalarga "sakrashlar" mavjud, masalan, async_write() ga o'tkazilgan lambda, bu kod ketma-ketligini buzadi va bu qanday tartibda bashorat qilishni imkonsiz qiladi. lambdalar deyiladi. Bu kodni o'qish va tushunishni qiyinlashtiradi.

Asinxron/kutish

Keling, asinxron kod yaratishga harakat qilaylik, shunda u sinxron ko'rinadi. Yaxshiroq tushunish uchun vazifani biroz o'zgartiraylik: endi biz tarmoq orqali uzatiladigan kalit yordamida ma'lumotlar bazasi va fayldan ma'lumotlarni o'qishimiz va natijani tarmoq orqali qaytarib yuborishimiz kerak.

Public async void work() ( var db_conn = Db_connection(localhost); var socket = Socket(localhost, port); socket.wait_connection(); var data = socket.async_read(); var db_data = db_conn.async_get(ma'lumotlarni kutish) ; var file_data = Fayl(ma'lumotlarni kutmoqda).async_read(); await socket.async_write($”(db_data kutmoqda) (fayl_data kutilmoqda)"); socket.close(); )

Keling, dasturni satr bo'yicha ko'rib chiqamiz:

  • Funktsiya sarlavhasidagi async kalit so'zi kompilyatorga funksiya asinxron ekanligini va uni boshqacha kompilyatsiya qilish kerakligini aytadi. U buni qanday amalga oshirishi quyida yozilgan.
  • Funktsiyaning dastlabki uchta qatori: ulanishni yaratish va kutish.
  • Keyingi satr asenkron o'qishni bajarishning asosiy oqimini to'xtatmasdan amalga oshiradi.
  • Keyingi ikkita satr ma'lumotlar bazasiga asinxron so'rov yuboradi va faylni o'qiydi. Kutish operatori ma'lumotlar bazasi va fayldan asinxron o'qish vazifasi tugamaguncha joriy funktsiyani to'xtatib turadi.
  • Oxirgi satrlar rozetkaga asinxron yozishni amalga oshiradi, lekin biz ma'lumotlar bazasi va fayldan asinxron o'qishni kutganimizdan keyingina.

Bu avval ma'lumotlar bazasini, keyin faylni ketma-ket kutishdan tezroq. Ko'pgina ilovalarda async/await ning ishlashi klassik qayta qo'ng'iroq funksiyalariga qaraganda yaxshiroq bo'ladi, shu bilan birga bunday kod sinxron sifatida o'qiladi.

Korutinlar

Yuqorida tavsiflangan mexanizm korutin deb ataladi. Siz ko'pincha "koroutin" variantini eshitishingiz mumkin (inglizcha coroutine - coroutine).

Bir nechta kirish nuqtalari

Aslida, koroutinlar bir nechta kirish va chiqish nuqtalariga ega bo'lgan funktsiyalardir. Oddiy funktsiyalarda faqat bitta kirish va bir nechta chiqish nuqtalari mavjud. Agar yuqoridagi misolga qaytadigan bo'lsak, birinchi kirish nuqtasi asinx operatori yordamida funksiya chaqiruvining o'zi bo'ladi, keyin funktsiya ma'lumotlar bazasi yoki faylni kutish o'rniga uning bajarilishini to'xtatadi. Barcha keyingi kutishlar funksiyani qayta ishga tushirmaydi, lekin avvalgi uzilish nuqtasida uning bajarilishini davom ettiradi. Ha, ko'p tillarda korutinda bir nechta kutishlar bo'lishi mumkin.

Yaxshiroq tushunish uchun Python-dagi kodni ko'rib chiqaylik:

Def async_factorial(): natija = 1, rost: natija natijasi *= i fac = async_factorial() diapazondagi i uchun(42): chop etish(keyingi(fac))

Dastur 0 dan 41 gacha bo'lgan raqamlar bilan faktoriy raqamlarning butun ketma-ketligini chiqaradi.

Async_factorial() funksiyasi keyingi() funksiyasiga o'tkazilishi mumkin bo'lgan generator ob'ektini qaytaradi, u barcha mahalliy funktsiya o'zgaruvchilari holatini saqlab, keyingi rentabellik bayonotigacha koroutinni bajarishni davom ettiradi. Keyingi() funktsiyasi koroutin ichidagi yield bayonoti o'tgan narsani qaytaradi. Shunday qilib, async_factorial() funktsiyasi, nazariy jihatdan, bir nechta kirish va chiqish nuqtalariga ega.

Stackful va Stackless

Stackdan foydalanishga qarab, koroutinlar har bir koroutin o'z stekiga ega bo'lgan stackful va barcha mahalliy funktsiya o'zgaruvchilari maxsus ob'ektda saqlanadigan stackless ga bo'linadi.

Koroutinlarda biz rentabellik bayonotini istalgan joyga qo'yishimiz mumkinligi sababli, biz funktsiyaning butun kontekstini biron bir joyda saqlashimiz kerak, bu stekdagi ramka (mahalliy o'zgaruvchilar) va boshqa meta-ma'lumotlar. Bu, masalan, stackful coroutinesda bo'lgani kabi, stekni to'liq almashtirish orqali amalga oshirilishi mumkin.

Quyidagi rasmda asinxron qo'ng'iroq yangi stek ramkasini yaratadi va unga ipning bajarilishini o'zgartiradi. Bu amalda yangi mavzu, faqat u asosiysi bilan asinxron ravishda bajariladi.

yield, o'z navbatida, oldingi stekdagi joriy freymning oxiriga havolani saqlab, oldingi stek ramkasini qaytaradi.

O'z stekingizga ega bo'lish sizga ichki funksiya qo'ng'iroqlaridan chiqish imkonini beradi, ammo bunday qo'ng'iroqlar steksiz koroutinlarga qaraganda sekinroq bo'lgan dasturni bajarish kontekstini to'liq yaratish/o'zgartirish bilan birga keladi.

Ko'proq samarali, lekin ayni paytda cheklanganroq, stackless koroutinlardir. Ular stekdan foydalanmaydi va kompilyator koroutinlarni o'z ichiga olgan funktsiyani koroutinsiz holat mashinasiga aylantiradi. Masalan, kod:

Def fib(): a = 0 b = 1, rost esa: hosil a a += b hosil b b += a

Quyidagi psevdokodga aylantiriladi:

Sinf fib: def __init__(self): self.a = 0 self.b = 1 self.__natija: int self.__state = 0 def __next__(self): while True: if self.__state == 0: self.a = 0 self.b = 1 agar self.__state == 0 yoki self.__state == 3: self.__result = self.a self.__state = 1 return self.__result if self.__state == 1: self.a += self.b self.__result = self.b self.__state = 2 qaytish self.__result if self.__state == 2: self.b += a self.__state = 3 tanaffus

Asosan, bu funksiyaning barcha holatini, shuningdek, rentabellik chaqirilgan oxirgi nuqtani saqlaydigan sinfni yaratadi. Ushbu yondashuv bilan bog'liq muammo bor: rentabellikni faqat koroutin funktsiya tanasi ichida chaqirish mumkin, lekin ichki o'rnatilgan funktsiyalar ichida emas.

Simmetrik va assimetrik

Korutinlar ham simmetrik va assimetrikga bo'linadi.

Simmetrik Barcha kutilayotgan asinxron operatsiyalar orasidan keyingi bajarilishi kerak bo'lganini tanlaydigan global korutin rejalashtiruvchiga ega bo'ling. Misol tariqasida wait_connection() funksiyasining boshida muhokama qilingan rejalashtiruvchini keltirish mumkin.

IN assimetrik koroutinlarda global rejalashtiruvchi yo'q va dasturchi kompilyatorni qo'llab-quvvatlashi bilan birgalikda qaysi koroutinni va qachon bajarilishini tanlaydi. Aksariyat korutin ilovalari assimetrikdir.

Xulosa

Asinxron dasturlash - tez-tez tizim kutishlari bilan yuqori yuklangan dasturlarni optimallashtirish uchun juda kuchli vosita. Ammo har qanday murakkab texnologiya kabi, uni faqat mavjud bo'lganligi uchun ishlatib bo'lmaydi. Siz har doim o'zingizga savol berishingiz kerak: menga bu texnologiya kerakmi? Bu menga qanday amaliy foyda beradi? Aks holda, ishlab chiquvchilar hech qanday foyda olmasdan ko'p kuch, vaqt va pul sarflashga xavf tug'diradilar.

Izoh: .NET Frameworkda asinxron dasturlash. EndOperation, Pooling, Callback usullari. Ixtiyoriy usulning asinxron ishga tushirilishi. Interfeys yangilanishi. Ko'p tarmoqli ilovalarning xavfsizligi. Sinxronizatsiya: avtomatik, qo'lda; sinxronlash maydonlaridan foydalanish. ProgressBar boshqaruvi

Biz "uzoq" usulni ishga tushirganimizda, protsessor uni bajaradi, shu bilan birga foydalanuvchining boshqa so'rovlarini bajarish uchun "vaqt yo'q" (7.2-rasm).

Sinxron rejimda ishlayotganda, dastur faqat bitta ipga ega. Yordamida asinxron model Dasturlashda siz bir nechta parallel iplarni ishga tushirishingiz va ular ishlayotgan vaqtda yangi foydalanuvchi harakatlariga munosabat bildirishingiz mumkin. n-ip bajarilgandan so'ng, siz natijani ekranda ko'rsatasiz.

Kundalik hayotda asenkron xatti-harakatlarning ko'plab misollari mavjud - omborda xom ashyoni to'ldirish zavodning uzluksiz ishlashini ta'minlashga imkon beradi. Sinxron bo'lmagan model xom ashyodan to'liq foydalanish va materialni etkazib berishni kutish vaqtida keyingi ishlamay qolish vaqtlari bo'ladi.

.NET Framework-da asinxron dasturlashni qo'llab-quvvatlash

O'rnatilgan qo'llab-quvvatlanadigan sinflar asinxron model, sinxron usullarning har biri uchun bir juft asinxron usullarga ega bo'ling. Bu usullar Begin va End so'zlari bilan boshlanadi. Misol uchun, agar biz System.IO.Stream sinfining Read usulining asinxron versiyasidan foydalanmoqchi bo'lsak, xuddi shu sinfning BeginRead va EndRead usullaridan foydalanishimiz kerak.

O'rnatilgan yordamdan foydalanish uchun asinxron model dasturlash uchun tegishli BeginOperation usulini chaqirishingiz va qo'ng'iroqni yakunlash modelini tanlashingiz kerak. BeginOperation usulini chaqirish asinxron operatsiyaning bajarilish holatini aniqlaydigan IAsyncResult interfeys ob'ektini qaytaradi. Asinxron usullarni tugatishning bir necha yo'li mavjud.

EndOperation usuli

EndOperation usuli asenkron qo'ng'iroqni tugatish uchun asosiy oqim asinxron usul chaqiruvi natijalaridan mustaqil bo'lgan katta hajmdagi hisoblashni bajarishi kerak bo'lganda ishlatiladi. Asosiy ish bajarilgandan so'ng va dastur keyingi harakatlar uchun asinxron usul natijalarini talab qilgandan so'ng, EndOperation usuli chaqiriladi. Bunday holda, asenkron usul tugagunga qadar asosiy ip to'xtatiladi. Ushbu usuldan foydalanishga misol:

Tizimdan foydalanish; System.IO dan foydalanish; nom maydoni EndOperation ( Class1 ( statik void Main(string args)) ( //Oqim yarating va faylni oching. FileStream fs = new FileStream("text.txt", FileMode.Open); bayt fileBytes = yangi bayt; // Ishga tushirish parallel ipda o'qish usuli.IAsyncResult ar = fs.BeginRead(fileBytes, 0, fileBytes.Length, null, null); for(int i = 0; i)<10000000; i++) { // Имитация длительной работы основного // потока, не зависящая от выполнения асинхронного метода. } // После завершения работы основного потока // запускаем завершение выполнения параллельного // метода Read. fs.EndRead(ar); Console.Write(System.Text.Encoding.Default.GetString(fileBytes)); } } } Листинг 7.1.

Parallel ip ar bajarilishini tugatish uchun bu yerda EndRead usuli chaqirildi. Uzoq muddatli ishni taqlid qiluvchi kod sifatida siz aniq vazifani bajarish hisoblagichini ulashingiz mumkin, biz buni "Windows shakllarida kod kutubxonalaridan foydalanish" da muhokama qildik.

Birlashtirish usuli

Ushbu usul asenkron usulning bajarilishini nazorat qilish kerak bo'lgan hollarda mos keladi. Undan foydalanganda, IAsyncResult tipidagi ob'ektning IsCompleted xususiyatidan foydalanib, asinxron usulning bajarilishini qo'lda tekshirishingiz kerak. Bu eng keng tarqalgan tugatish usuli emas, chunki ko'pchilik jarayonlar ijro nazoratini talab qilmaydi. Ovoz berish usulidan foydalanishga misol:

Tizimdan foydalanish; System.IO dan foydalanish; nomlar maydoni Pooling ( Class1 ( statik void Main(string args) sinfi) ( FileStream fs = new FileStream("text.txt", FileMode.Open); bayt fileBytes = yangi bayt; Console.Write("O'qish usulini asinxron tarzda bajarish.") ; // Read usulini asinxron tarzda ishga tushiring.IAsyncResult ar = fs.BeginRead(fileBytes, 0, fileBytes.Length, null, null); // Asinxron usulning bajarilishini tekshiring. while(!ar.IsCompleted) ( // While ekranda faylni o'qishda Console.Write("Jarayon davom etmoqda"); ) Console.WriteLine(); string textFromFile = System.Text.Encoding.Default.GetString(fileBytes); Console.Write(textFromFile); ) ) ) Ro‘yxat 7.2.

Katta ehtimol bilan, jarayon davomida siz yozuvning ko'rinishini sezmaysiz - fayl juda tez o'qiladi. Ushbu usulni sinab ko'rish uchun floppi diskga katta matn faylini yozing, manzilni ko'rsating va dasturni qayta ishga tushirib ko'ring.

Qayta qo'ng'iroq qilish usuli

Asinxron qo'ng'iroqni tugatishning qayta qo'ng'iroq qilish usuli siz asosiy oqimni blokirovka qilishni oldini olishni xohlagan hollarda qo'llaniladi. Callback dan foydalanilganda, biz EndOperation usulini metodning tanasida ishga tushiramiz, bu parallel ipda ishlaydigan usul tugagach chaqiriladi. Chaqirilayotgan usulning imzosi AsyncCallback delegati imzosiga mos kelishi kerak.

Qayta qo'ng'iroq qilish opsiyasidan foydalanishga misol:

Tizimdan foydalanish; System.IO dan foydalanish; namespace Callback ( sinf Class1 ( // Oqim va baytlar massivi yarating. statik FileStream fs; statik bayt fileBytes; statik void Main(string args) ( // Faylni oqimga oching. fs = new FileStream("matn. txt", FileMode. Open); fileBytes = yangi bayt; // WorkComplete usuli fs.BeginRead(fileBytes, 0, (int)fs.Length, new AsyncCallback(WorkComplete), null); ) orqali o'qish usulini asinxron tarzda ishga tushiring. // /

/// Parallel ip tugashi bilan avtomatik ravishda chaqiriladigan usul. /// /// IAsyncResult turidagi ob'ekt. statik void WorkComplete(IAsyncResult ar) ( // Usulning oxirini ishga tushiring. fs.EndRead(ar); string textFromFile = System.Text.Encoding.Default.GetString(fileBytes); Console.Write(textFromFile); ) ) ) Ro'yxat 7.3.

Kitobga kiritilgan diskda siz EndOperation, Pooling va Callback ilovalarini topasiz (Code\Glava7\EndOperation, Pooling, Callback).

Oxirgi yangilanish: 17.10.2018

Asinxroniya alohida vazifalarni asosiy oqimdan maxsus asinxron usullarga yoki kod bloklariga o'tkazish imkonini beradi. Bu, ayniqsa, uzoq vazifalar foydalanuvchi interfeysini bloklashi mumkin bo'lgan grafik dasturlarda to'g'ri keladi. Va bu sodir bo'lishining oldini olish uchun siz asinxroniyadan foydalanishingiz kerak. Asinxroniya foydalanuvchilarning so'rovlarini qayta ishlashda, ma'lumotlar bazalari yoki tarmoq resurslariga kirishda veb-ilovalarda ham afzalliklarga ega. Ma'lumotlar bazasiga katta so'rovlar uchun asinxron usul ma'lumotlar bazasidan ma'lumotlarni olguncha bir muncha vaqt uxlab qoladi va asosiy ip o'z ishini davom ettirishi mumkin. Sinxron dasturda, agar ma'lumotni qabul qilish uchun kod asosiy oqimda bo'lsa, ma'lumotlarni qabul qilish paytida bu oqim shunchaki bloklanadi.

C# da asinxron qo'ng'iroqlar bilan ishlashning kaliti ikkita kalit so'zdir: asinxron va kutish, ularning maqsadi asinxron kod yozishni osonlashtirishdir. Ular asenkron usulni yaratish uchun birgalikda ishlatiladi.

Asinxron usul quyidagi xususiyatlarga ega:

    Usul sarlavhasi async modifikatoridan foydalanadi

    Usul bir yoki bir nechta kutish ifodalarini o'z ichiga oladi

    Qaytish turi quyidagilardan biridir:

    • ValueTask

Asinxron usul, odatdagidek, istalgan miqdordagi parametrlardan foydalanishi yoki umuman foydalanmasligi mumkin. Biroq, asinxron usul parametrlarni out va ref modifikatorlari bilan aniqlay olmaydi.

Shuni ham ta'kidlash joizki, usul ta'rifida ko'rsatilgan async so'zi avtomatik ravishda usulni asinxron qilmaydi. Bu shunchaki usul bir yoki bir nechta kutish ifodalarini o'z ichiga olishi mumkinligini ko'rsatadi.

Keling, asinxron usulning misolini ko'rib chiqaylik:

Tizimdan foydalanish; System.Threading yordamida; System.Threading.Tasks dan foydalanish; nom maydoni HelloApp ( sinf dasturi ( statik void Faktorial() ( int natija = 1; for(int i = 1; i)<= 6; i++) { result *= i; } Thread.Sleep(8000); Console.WriteLine($"Факториал равен {result}"); } // определение асинхронного метода static async void FactorialAsync() { Console.WriteLine("Начало метода FactorialAsync"); // выполняется синхронно await Task.Run(()=>Faktorial()); // asinxron bajarildi Console.WriteLine("FactorialAsync usulining oxiri"); ) statik void Main(string args) ( FactorialAsync(); // asinxron usulni chaqirish Console.WriteLine("Raqamni kiriting: "); int n = Int32.Parse(Console.ReadLine()); Console.WriteLine($ "Kvadrat raqam (n * n) ga teng"); Console.Read(); ) ) )

Bu erda, birinchi navbatda, faktorialni hisoblashning odatiy usuli aniqlanadi. Uzoq ish vaqtlarini taqlid qilish uchun u Thread.Sleep() usuli yordamida 8 soniya kechikishdan foydalanadi. An'anaviy ravishda, bu uzoq vaqt davomida ba'zi ishlarni bajaradigan usul. Ammo tushunishni osonlashtirish uchun u 6 ning faktorialini hisoblab chiqadi.

Shuningdek, u FactorialAsync() asinxron usulini belgilaydi. U asinxrondir, chunki u taʼrifda qaytish turi oldida asinxron modifikatorga ega, uning qaytish turi bekor va await ifodasi usul tanasida aniqlangan.

Kutish ifodasi asinxron bajariladigan vazifani bildiradi. Bunday holda, shunga o'xshash vazifa omil funktsiyasini amalga oshirishni anglatadi:

Task.Run(()=>Faktorial());

Ochilmagan qoidalarga ko'ra, asinxron usullar nomida Async - FactorialAsync () qo'shimchasidan foydalanish odatiy holdir, garchi printsipial jihatdan bu kerak emas.

FactorialAsync asinxron usulida faktorialni olamiz. U asinxrondir, chunki u asinxron modifikator bilan e'lon qilingan va await kalit so'zidan foydalanishni o'z ichiga oladi.

Va Main usulida biz buni asinxron usul deb ataymiz.

Keling, dasturning konsol chiqishi qanday bo'lishini ko'rib chiqaylik:

FactorialAsync usulining boshlanishi Raqamni kiriting: 7 Raqamning kvadrati 49 Asosiy usulning oxiri Faktorial 720 FactorialAsync usulining oxiri

Keling, bu erda nima sodir bo'layotganini bosqichma-bosqich ko'rib chiqaylik:

    Asinxron FactorialAsync usulini chaqiruvchi Main usuli ishga tushiriladi.

    FactorialAsync usuli kutish ifodasi paydo bo'lguncha sinxron tarzda ishlay boshlaydi.

    Kutish ifodasi asinxron vazifani bajaradi. Task.Run(()=>Faktorial())

    Task.Run(()=>Factorial()) asinxron vazifasi ishlayotganda (va u ancha vaqt ishlashi mumkin), kodning bajarilishi chaqiruv usuliga, ya’ni Main metodiga qaytadi. Main usulida bizdan sonning kvadratini hisoblash uchun raqam kiritish talab qilinadi.

    Bu asinxron usullarning afzalligi - juda uzoq vaqt ishlashi mumkin bo'lgan asinxron vazifa Main usulini bloklamaydi va biz u bilan ishlashni davom ettirishimiz mumkin, masalan, ma'lumotlarni kiritish va qayta ishlash.

    Asinxron topshiriq o'z bajarilishini tugatgandan so'ng (yuqoridagi holatda u raqamning faktorialini hisoblab chiqdi), asinxron vazifa deb nomlangan asinxron FactorialAsync usuli ishlashda davom etadi.

Faktorial funktsiya eng yaxshi misol bo'lmasligi mumkin, chunki aslida bu holda uni asinxron qilishning ma'nosi yo'q. Ammo yana bir misolni ko'rib chiqaylik - faylni o'qish va yozish:

Tizimdan foydalanish; System.Threading yordamida; System.Threading.Tasks dan foydalanish; System.IO dan foydalanish; nom maydoni HelloApp ( class Program ( statik async void ReadWriteAsync() ( string s = "Salom dunyo! Bir vaqtning o'zida bir qadam"; // hello.txt - (StreamWriter writer = new StreamWriter(") yordamida yoziladigan va o'qiladigan fayl salom .txt", noto'g'ri)) ( writer.WriteLineAsync(lar); // faylga asinxron yozish ) (StreamReader o'quvchi = new StreamReader("hello.txt")) ( string result = await reader.ReadToEndAsync() ; / / fayldan asinxron o'qish Console.WriteLine(natija); ) ) statik void Main(string args) ( ReadWriteAsync(); Console.WriteLine("Ba'zi ishlar"); Console.Read(); ) ) )

Asinxron ReadWriteAsync() usuli faylga satr yozadi va keyin yozilgan faylni o'qiydi. Bunday operatsiyalar, ayniqsa, katta hajmdagi ma'lumotlar bilan uzoq vaqt talab qilishi mumkin, shuning uchun bunday operatsiyalarni asinxron tarzda bajarish yaxshiroqdir.

.NET Framework allaqachon bunday operatsiyalar uchun o'rnatilgan yordamga ega. Masalan, StreamWriter klassi WriteLineAsync() usulini belgilaydi. Aslida, u allaqachon asinxron operatsiyani ifodalaydi va faylga yozilishi kerak bo'lgan ma'lum bir qatorni parametr sifatida oladi. Ushbu usul asenkron operatsiyani ifodalaganligi sababli, biz ushbu usulga qo'ng'iroqni kutish ifodasi sifatida belgilashimiz mumkin:

writer.WriteLineAsync(lar)ni kuting; // faylga asinxron yozish

Xuddi shunday, StreamReader klassi ReadToEndAsync() usulini belgilaydi, u ham asinxron operatsiyani ifodalaydi va barcha o'qilgan matnni qaytaradi.

.NET Core ramkasi ko'plab shunga o'xshash usullarni belgilaydi. Qoida tariqasida, ular fayllar bilan ishlash, tarmoq so'rovlarini yoki ma'lumotlar bazasi so'rovlarini yuborish bilan bog'liq. Ular Async qo'shimchasi bilan osongina tan olinadi. Ya'ni, agar usul o'z nomida shunga o'xshash qo'shimchaga ega bo'lsa, u holda kutish iborasida foydalanish ehtimoli ko'proq.

Statik void Main(string args) ( ReadWriteAsync(); Console.WriteLine("Ba'zi ishlar"); Console.Read(); )

Yana ReadWriteAsync usulida bajarilish birinchi kutish ifodasiga yetganda, boshqaruv Main usuliga qaytadi va biz u bilan ishlashni davom ettirishimiz mumkin. Faylga yozish va faylni o'qish parallel ravishda amalga oshiriladi va Main usulining ishlashini bloklamaydi.

Asinxron operatsiyani aniqlash

Yuqorida aytib o'tilganidek, .NET Core ramkasida asinxron operatsiyani ifodalovchi ko'plab o'rnatilgan usullar mavjud. Ular Async qo'shimchasi bilan tugaydi. Va bunday usullarni chaqirishdan oldin biz kutish operatorini belgilashimiz mumkin. Masalan:

StreamWriter yozuvchisi = yangi StreamWriter("hello.txt", noto'g'ri); writer.WriteLineAsync("Salom"); // faylga asinxron yozish

Yoki biz Task.Run() usuli yordamida asinxron operatsiyani o'zimiz belgilashimiz mumkin:

Statik void Faktorial() ( int natija = 1; for (int i = 1; i)<= 6; i++) { result *= i; } Thread.Sleep(8000); Console.WriteLine($"Факториал равен {result}"); } // определение асинхронного метода static async void FactorialAsync() { await Task.Run(()=>Faktorial()); // asinxron operatsiyani chaqirish)

Lambda ifodasi yordamida asinxron operatsiyani belgilashingiz mumkin:

Statik async void FactorialAsync() ( kuting Task.Run(() => ( int natija = 1; for (int i = 1; i)<= 6; i++) { result *= i; } Thread.Sleep(8000); Console.WriteLine($"Факториал равен {result}"); }); }

Parametrlarni asinxron operatsiyaga o'tkazish

Yuqorida biz 6 ning faktorialini hisoblab chiqdik, lekin aytaylik, biz turli raqamlarning faktoriallarini hisoblamoqchimiz:

Tizimdan foydalanish; System.Threading yordamida; System.Threading.Tasks dan foydalanish; nom maydoni HelloApp ( sinf dasturi ( statik void Faktorial(int n) ( int natija = 1; uchun (int i = 1; i)<= n; i++) { result *= i; } Thread.Sleep(5000); Console.WriteLine($"Факториал равен {result}"); } // определение асинхронного метода static async void FactorialAsync(int n) { await Task.Run(()=>Faktorial(n)); ) statik void Main(string args) ( FactorialAsync(5); FactorialAsync(6); Console.WriteLine("Ba'zi ishlar"); Console.Read(); ) ) )

Natijani asinxron operatsiyadan olish

Asinxron operatsiya ba'zi bir natijani qaytarishi mumkin, biz buni oddiy usulni chaqirganda bo'lgani kabi olishimiz mumkin:

Tizimdan foydalanish; System.Threading yordamida; System.Threading.Tasks dan foydalanish; nom maydoni HelloApp ( sinf dasturi ( statik int Factorial(int n) ( int natija = 1; uchun (int i = 1; i)<= n; i++) { result *= i; } return result; } // определение асинхронного метода static async void FactorialAsync(int n) { int x = await Task.Run(()=>Faktorial(n)); Console.WriteLine($"Faktorial (x)"); ) statik void Main(string args) ( FactorialAsync(5); FactorialAsync(6); Console.Read(); ) ) )

Faktorial usul int tipidagi qiymatni qaytaradi, biz bu qiymatni oddiygina asinxron operatsiya natijasini shu turdagi o'zgaruvchiga belgilash orqali olishimiz mumkin: int x = await Task.Run(()=>Faktorial(n));

Bir muncha vaqtdan beri menga asinxron dasturlash haqida juda ko'p savollar paydo bo'ldi. Va bu mavzu ko'plab o'quvchilarimni qiziqtirayotganini tushunib, men ushbu atamalarni tushuntirish uchun maqola yozishga qaror qildim, ayniqsa Asinxron dasturlash zamonaviy Internetning juda muhim qismidir.

Boshlash uchun shuni ta'kidlash kerakki, ikkita mutlaqo boshqa tushunchalar mavjud: birinchisi sinxron va asinxron dasturlash modellari, ikkinchisi esa - bitta va ko'p tarmoqli muhitlar. Dasturlash modellarining har biri (sinxron va asinxron) ham bitta va ko'p tarmoqli muhitda ishlashi mumkin.

Sinxron dasturlash modeli.

Ushbu dasturlash modelida ip bitta vazifaga tayinlanadi va u ustida ishlay boshlaydi. Vazifa tugallangandan so'ng, ip keyingi vazifa uchun mavjud bo'ladi. Bular. bir vazifa boshqasi bilan ketma-ket almashtiriladi. Ushbu modelda boshqa vazifani bajarish uchun vazifani o'rtada qoldirish mumkin emas. Keling, ushbu model bitta va ko'p tarmoqli muhitlarda qanday ishlashini muhokama qilaylik.

Yagona tishli muhit- agar bizda bajarilishi kerak bo'lgan bir nechta vazifa bo'lsa va joriy tizim faqat bitta ipni ta'minlasa, u holda vazifalar ipga birma-bir beriladi. Buni vizual tarzda quyidagicha tasvirlash mumkin:

Qayerda Mavzu 1 - bitta ip, 1-topshiriq va 2-topshiriq, 3-topshiriq, 4-topshiriq – tegishli vazifalar.

Bizda oqim borligini ko'ramiz ( Mavzu 1) Va to'rtta vazifa bu yakunlanishi kerak. Ip vazifalar ustida ishlay boshlaydi va barcha vazifalarni birma-bir bajaradi.

Multi-threaded muhiti - Multi-threaded- bu muhitda biz bir vaqtning o'zida ushbu vazifalarni bajara oladigan bir nechta iplardan foydalanamiz. Bu bizda borligini anglatadi iplar hovuzi(mavjud manbalar asosida kerak bo'lganda yangi mavzular ham yaratilishi mumkin) va bir nechta vazifalar.

Bizda to'rtta ip va bir xil miqdordagi vazifalar borligini ko'ramiz. Shuning uchun har bir ip bitta vazifani bajaradi va uni yakunlaydi. Bu ideal stsenariy, ammo oddiy sharoitlarda biz mavjud mavzular sonidan ko'ra ko'proq vazifalarga ega bo'lamiz. Shunday qilib, bir ish zarrachasi ba'zi bir vazifani bajarishni tugatsa, u darhol boshqasini bajarishni boshlaydi. Shuni ham yodda tutingki, yangi ip har safar yaratilmaydi, chunki u CPU davrlari va xotira kabi tizim resurslarini talab qiladi, bu etarli bo'lmasligi mumkin.

Endi asinxron model va uning bir va ko‘p tarmoqli muhitda o‘zini qanday tutishi haqida gapiraylik.

Asinxron dasturlash modeli.

Sinxron dasturlash modelidan farqli o'laroq, bu erda ma'lum bir vazifani boshlagan bitta ip o'zining hozirgi holatini saqlab qolgan holda, ma'lum vaqt davomida bajarilishini to'xtatib qo'yishi va boshqa vazifani bajarishni boshlashi mumkin.

biz bitta ip barcha vazifalarni bajarish, ularni bir-biri bilan almashtirish uchun javobgar ekanligini ko'ramiz.

Agar bizning tizimimiz bir nechta iplarni yaratishga qodir bo'lsa, unda barcha iplar asenkron modelda ishlashi mumkin.

Biz bir xil vazifalarni ko'ramiz T4, T5, T6 bir nechta iplar tomonidan qayta ishlanadi. Bu skriptning go'zalligi va kuchi. Ko'rib turganingizdek, vazifa T4 mavzuda birinchi bo'lib ishga tushirildi Mavzu 1 va mavzuda yakunlandi Mavzu 2. Xuddi shunday T6 da tugaydi Mavzu 2, mavzu 3 va mavzu 4.

Shunday qilib, bizda jami to'rtta stsenariy bor -

  • Sinxron yagona ip
  • Sinxron ko'p tarmoqli
  • Asinxron bitta ip
  • Asinxron ko'p tarmoqli

Asinxron dasturlashning afzalliklari

Har qanday dastur uchun ikkita narsa muhim: qulaylik va ishlash. Foydalanish qulayligi muhim, chunki foydalanuvchi ba'zi ma'lumotlarni saqlash uchun tugmani bosganda, u bir nechta kichik vazifalarni bajarishni talab qiladi, masalan, ichki ob'ektdagi ma'lumotlarni o'qish va to'ldirish, SQL serveriga ulanish va so'rovni u erda saqlash va hokazo. . va boshqalar.

Chunki SQL server, masalan, tarmoqdagi boshqa kompyuterda ishlayotgan va boshqa jarayon ostida ishlayotgan bo'lsa, bu uzoq vaqt talab qilishi mumkin. Va agar dastur bitta ipda ishlayotgan bo'lsa, u holda foydalanuvchi qurilmasi ekrani barcha vazifalar bajarilmaguncha faol bo'lmagan holatda qoladi, bu juda yomon foydalanuvchi interfeysiga misoldir. Shuning uchun ko'plab ilovalar va yangi ramkalar to'liq asinxron modelga tayanadi, chunki u sezgir interfeysni saqlab, ko'plab vazifalarni bajarishga imkon beradi.

Ilovaning samaradorligi ham juda muhimdir. Hisob-kitoblarga ko'ra, so'rovni bajarishda vaqtning taxminan 70-80 foizi bog'liq vazifalarni kutish uchun yo'qoladi. Shuning uchun, bu erda asinxron dasturlash foydali bo'ladi.

Shunday qilib, ushbu maqolada biz sinxron va asinxron dasturlash nima ekanligini ko'rib chiqdik. Asinxron dasturlashga alohida e'tibor qaratildi, chunki u zamonaviy ishlab chiqish vositalarining aksariyat qismini tashkil etadi. Va keyingi maqolalarda biz asinxron modeldan foydalangan holda haqiqiy misollar bilan tanishamiz.

Oxirgi yangilangan: 31.10.2015

Oldingi mavzular asenkron va kutish kalit so'zlari yordamida asinxroniyadan foydalanishni qamrab olgan. Ammo C# da asinxron qo'ng'iroqlardan foydalanishning ushbu modeliga qo'shimcha ravishda, boshqa model mavjud - asinxron delegatlardan foydalanish. Asinxron delegatlar C# da asinxron va kutishni joriy qilishdan oldin keng qo'llanilgan, ammo endi asinxron va kutish asinxron kodni yozishni ancha osonlashtiradi. Biroq, asinxron delegatlar hali ham ishlatilishi mumkin. Shunday qilib, keling, ularni ko'rib chiqaylik.

Asinxron delegatlar delegatlar ishora qilgan usullarni asinxron chaqirishga imkon beradi. Delegatlar haqidagi mavzuda delegatlarni Invoke usuli yordamida yoki BeginInvoke/EndInvoke juftligi yordamida asinxron tarzda chaqirish mumkinligi aytilgan. Keling, bir misolni ko'rib chiqaylik. Birinchidan, agar biz ilovamizda odatiy sinxron koddan foydalansak nima bo'lishini ko'rib chiqamiz:

Tizimdan foydalanish; System.Threading yordamida; namespace AsyncApp ( class Program ( public delegate int DisplayHandler(); statik void Main(string args) ( DisplayHandler handler = new DisplayHandler(Display); int result = handler.Invoke(); Console.WriteLine("Asosiy usul davom etmoqda") ; Console.WriteLine("Natija (0)", natija); Console.ReadLine(); ) static int Display() ( Console.WriteLine("Display usuli ishlay boshlaydi..."); int natija = 0 ; uchun (int i = 1; i< 10; i++) { result += i * i; } Thread.Sleep(3000); Console.WriteLine("Завершается работа метода Display...."); return result; } } }

Bu raqamni qaytaradigan parametrsiz usulni mos yozuvlar sifatida qabul qiladigan maxsus DisplayHandler delegatini yaratadi. Bunday holda, bu usul ba'zi ishlarni bajaradigan Displey usulidir. Bunday holda, biz quyidagi natijaga o'xshash narsani olamiz:

Displey usuli ishlay boshlaydi Displey usuli tugaydi Main metodi ishlashda davom etadi.Natija 285

Umuman olganda, siz delegatdan foydalana olmadingiz va Displey usulini to'g'ridan-to'g'ri chaqira olmaysiz. Ammo har qanday holatda, uni chaqirgandan so'ng, Main usulining ishi Displey usulining bajarilishi tugagunga qadar bloklanadi.

Endi asinxron delegat chaqiruvlaridan foydalangan holda misolni o'zgartiramiz:

Tizimdan foydalanish; System.Threading yordamida; nom maydoni AsyncApp ( sinf dasturi ( ommaviy delegat int DisplayHandler(); statik void Main(string args) ( DisplayHandler ishlovchi = yangi DisplayHandler(Display); IAsyncResult resultObj = handler.BeginInvoke(null, null); Console.WriteLine("Usul davom etmoqda. Main"); int natija = handler.EndInvoke(resultObj); Console.WriteLine("Natija (0)", natija); Console.ReadLine(); ) static int Display() ( Console.WriteLine("Usul ishlay boshlaydi Displey...."); int result = 0; for (int i = 1; i< 10; i++) { result += i * i; } Thread.Sleep(3000); Console.WriteLine("Завершается работа метода Display...."); return result; } } }

Harakatlarning mohiyati deyarli o'zgarishsiz qoldi, xuddi shu Displey usuli, faqat endi u BedinInvoke/EndInvoke usullaridan foydalangan holda asinxron deb ataladi. Va endi biz biroz boshqacha natijani olishimiz mumkin:

Displey usuli ishlay boshlaydi.Main metodi ishlashda davom etadi.Display metodi tugaydi.Natija 285

Shunday qilib, Handler.BeginInvoke(null, null) ifodasi orqali Display usulini chaqirgandan so'ng, Main usuli to'xtatilmaydi. Va DisplayHandler delegati orqali Display usulining bajarilishi boshqa ish zarrachasida sodir bo'ladi. Va faqat Main usulida bajarilish qatorga yetganda int result = handler.EndInvoke(resultObj); u bloklaydi va Displey usulining bajarilishini kutadi.

Endi BeginInvoke va EndInvoke usullari va IAsyncResult interfeysidan foydalanish xususiyatlarini ko'rib chiqamiz.