NET: Kayan aiki don aiki tare da multithreading da asynchony. Kashi na 1

Ina buga ainihin labarin akan Habr, wanda aka buga fassararsa a cikin kamfani shafi.

Bukatar yin wani abu a daidaita, ba tare da jiran sakamakon nan da yanzu ba, ko rarraba manyan ayyuka tsakanin raka'o'i da yawa da ke aiwatar da shi, ya wanzu kafin zuwan kwamfutoci. Da zuwan su, wannan bukata ta zama ta zahiri. Yanzu, a cikin 2019, Ina buga wannan labarin akan kwamfutar tafi-da-gidanka tare da 8-core Intel Core processor, wanda fiye da matakai ɗari ke gudana a layi daya, har ma da ƙarin zaren. A kusa, akwai wata waya mai ban tsoro, wacce aka saya shekaru biyu da suka gabata, tana da na'ura mai sarrafawa 8-core a cikin jirgin. Abubuwan da ke da alaƙa suna cike da labarai da bidiyoyi inda marubutan su ke sha'awar wayowin komai da ruwan na bana waɗanda ke da na'urori masu sarrafawa 16-core. MS Azure yana ba da injin kama-da-wane tare da core processor 20 da RAM TB 128 akan ƙasa da $2 / awa. Abin baƙin ciki, ba shi yiwuwa a cire iyakar da kuma amfani da wannan ikon ba tare da samun damar gudanar da hulɗar zaren ba.

Terminology

Tsari - Abun OS, sararin adireshi keɓe, ya ƙunshi zaren.
Zare - abu OS, mafi ƙarami naúrar aiwatarwa, wani ɓangare na tsari, zaren raba ƙwaƙwalwar ajiya da sauran albarkatu a tsakanin su a cikin tsari.
Yin abubuwa da yawa - OS dukiya, ikon gudanar da matakai da yawa lokaci guda
Multi-core - dukiya na processor, ikon yin amfani da nau'i-nau'i da yawa don sarrafa bayanai
Multiprocessing - mallakin kwamfuta, ikon yin aiki lokaci guda tare da na'urori masu sarrafawa da yawa a jiki
Multithreading - dukiya na tsari, ikon rarraba sarrafa bayanai tsakanin zaren da yawa.
Daidaituwa - yin ayyuka da yawa a jiki lokaci guda a kowace raka'a na lokaci
Asynchony - aiwatar da aiki ba tare da jiran kammala wannan aiki ba, za a iya sarrafa sakamakon aiwatar da shi daga baya.

Misali

Ba duk ma'anoni ba ne masu kyau kuma wasu suna buƙatar ƙarin bayani, don haka zan ƙara misali game da dafa karin kumallo zuwa ƙa'idar da aka gabatar. Dafa karin kumallo a cikin wannan misalin tsari ne.

Yayin da nake shirya karin kumallo da safe na (CPUNa zo kitchen (Kwamfuta). Ina da hannaye 2 (Cores). Akwai na'urori da yawa a cikin kicin (IO): tanda, tukwane, kayan girki, firiji. Na kunna gas din, na dora kwanon frying sannan na zuba mai a ciki ba tare da jiran ya huce ba (asynchronously, Rashin Kashewa-IO-Jira), Ina fitar da ƙwai daga cikin firij in karya su cikin faranti, sannan in doke su da hannu ɗaya (Zare#1), da na biyu (Zare#2) rike da farantin (Shared Resource). Yanzu ina so in kunna kettle, amma ba ni da isassun hannaye (Zare Yunwa) A wannan lokacin, kwanon frying yana zafi (Processing the result) a cikinsa na zuba abin da na bulala. Na kai hannu na dauko tulun na kunna ina kallon yadda ruwan ke tafasa a cikinta cikin wauta.Toshe-IO-Jira), ko da yake a wannan lokacin zai iya wanke farantin inda ya yi bulala da omelet.

Na dafa omelette ta amfani da hannaye 2 kawai, kuma ba ni da ƙari, amma a lokaci guda, a lokacin da ake bulala omelette, an gudanar da ayyuka 3 a lokaci ɗaya: bulala omelet, rike da farantin, dumama kwanon frying. CPU shine bangaren da ya fi sauri a cikin kwamfuta, IO shine mafi yawan komai yana raguwa, don haka sau da yawa mafita mai inganci shine shagaltar da CPU da wani abu yayin karbar bayanai daga IO.

Ci gaba da misalin:

  • Idan a cikin tsarin shirya omelet, zan kuma yi ƙoƙarin canza tufafi, wannan zai zama misali na multitasking. Wani muhimmin nuance: kwamfutoci sun fi mutane kyau a wannan.
  • Gidan dafa abinci tare da chefs da yawa, alal misali a cikin gidan abinci - kwamfuta mai mahimmanci.
  • Yawancin gidajen cin abinci a cikin kotun abinci a cikin cibiyar kasuwanci - cibiyar bayanai

Kayan aikin NET

NET yana da kyau a aiki tare da zaren, kamar yadda yake da sauran abubuwa. Tare da kowane sabon juzu'i, yana gabatar da sabbin kayan aiki don aiki tare da su, sabbin yadudduka na abstraction akan zaren OS. Lokacin aiki tare da gina abubuwan ɓoye, masu haɓaka tsarin suna amfani da hanyar da ta bar damar, lokacin amfani da babban matakin abstraction, don sauka ɗaya ko fiye matakan ƙasa. Mafi sau da yawa wannan ba lallai ba ne, a gaskiya yana buɗe kofa don harbi kanka a ƙafa da bindiga, amma wani lokaci, a lokuta da yawa, yana iya zama hanya ɗaya tilo don magance matsalar da ba a warware ba a halin yanzu na abstraction. .

Ta kayan aiki, Ina nufin duka aikace-aikacen shirye-shiryen musaya (APIs) waɗanda tsarin tsari da fakiti na ɓangare na uku ke bayarwa, da kuma duk hanyoyin magance software waɗanda ke sauƙaƙe binciken duk wata matsala da ke da alaƙa da lambar zare da yawa.

Fara zaren

Ajin zaren shine mafi mahimmanci ajin a cikin NET don aiki tare da zaren. Mai ginin yana karɓar ɗaya daga cikin wakilai biyu:

  • ThreadStart - Babu sigogi
  • ParametrizedThreadStart - tare da siga guda ɗaya na nau'in abu.

Za a aiwatar da wakilin a cikin sabon zaren da aka ƙirƙira bayan an kira hanyar Fara. Idan wakili na nau'in ParametrizedThreadStart ya kasance zuwa ginin ginin, to dole ne a wuce wani abu zuwa hanyar Fara. Ana buƙatar wannan hanyar don canja wurin kowane bayanin gida zuwa rafi. Ya kamata a lura cewa ƙirƙirar zaren aiki ne mai tsada, kuma zaren kanta abu ne mai nauyi, aƙalla saboda yana ware 1MB na ƙwaƙwalwar ajiya akan tari kuma yana buƙatar hulɗa tare da OS API.

new Thread(...).Start(...);

Ajin ThreadPool yana wakiltar manufar tafkin. A cikin NET, tafkin zaren wani yanki ne na injiniya, kuma masu haɓakawa a Microsoft sun yi ƙoƙari sosai don tabbatar da cewa yana aiki da kyau a cikin yanayi iri-iri.

Babban ra'ayi:

Daga lokacin da aikace-aikacen ya fara, yana ƙirƙirar zaren da yawa a ajiye a bango kuma yana ba da damar ɗaukar su don amfani. Idan ana amfani da zaren akai-akai kuma a cikin adadi mai yawa, tafkin yana faɗaɗa don biyan bukatun mai kira. Lokacin da babu zaren kyauta a cikin tafkin a daidai lokacin, ko dai zai jira ɗayan zaren ya dawo, ko kuma ya ƙirƙiri wani sabo. Hakan ya biyo bayan tafkin zaren yana da kyau don wasu ayyuka na ɗan gajeren lokaci kuma bai dace da ayyukan da ke gudana azaman ayyuka a duk tsawon aikin aikace-aikacen ba.

Don amfani da zaren daga tafkin, akwai hanyar QueueUserWorkItem wanda ke karɓar wakilcin nau'in WaitCallback, wanda ke da sa hannu iri ɗaya da ParametrizedThreadStart, kuma ma'aunin da aka wuce zuwa gare shi yana aiki iri ɗaya.

ThreadPool.QueueUserWorkItem(...);

Ana amfani da mafi ƙarancin sanannun hanyar tafkin zare RegisterWaitForSingleObject don tsara ayyukan IO marasa toshewa. Za a kira wakilin da aka wuce zuwa wannan hanyar lokacin da WaitHandle ya wuce zuwa hanyar "An Saki".

ThreadPool.RegisterWaitForSingleObject(...)

NET yana da mai ƙidayar zare kuma ya bambanta da WinForms/WPF masu ƙidayar lokaci a cikin cewa za a kira mai kula da shi akan zaren da aka ɗauka daga tafkin.

System.Threading.Timer

Hakanan akwai wata hanya mai ban mamaki don aika wakilai don aiwatarwa zuwa zaren daga tafkin - hanyar BeginInvoke.

DelegateInstance.BeginInvoke

Ina so in ɗan taƙaita aikin da za a iya kiran yawancin hanyoyin da ke sama - CreateThread daga Kernel32.dll Win32 API. Akwai wata hanya, godiya ga tsarin hanyoyin waje, don kiran wannan aikin. Na ga irin wannan kiran sau ɗaya kawai a cikin mummunan misali na lambar gado, kuma kwarin gwiwar marubucin wanda ya yi daidai wannan har yanzu ya kasance wani asiri a gare ni.

Kernel32.dll CreateThread

Dubawa da Gyara Zaren

Zaren da ka ƙirƙira, duk abubuwan da aka haɗa na ɓangare na uku, da kuma .NET pool ana iya ganin su a cikin tagar Zauren Kayayyakin Kayayyakin Kayayyakin Kaya. Wannan taga za ta nuna bayanan zaren ne kawai lokacin da aikace-aikacen ke ƙarƙashin debug kuma a yanayin Break. Anan zaku iya dacewa da duba tarin sunaye da abubuwan fifiko na kowane zaren, kuma canza gyara zuwa takamaiman zaren. Yin amfani da kayan fifiko na ajin Zaren, zaku iya saita fifikon zaren, wanda OC da CLR za su fahimta azaman shawarwarin lokacin rarraba lokacin sarrafawa tsakanin zaren.

NET: Kayan aiki don aiki tare da multithreading da asynchony. Kashi na 1

Task Parallel Library

An gabatar da Laburaren Task Parallel (TPL) a cikin NET 4.0. Yanzu shine ma'auni kuma babban kayan aiki don aiki tare da asynchony. Duk wani lambar da ke amfani da tsohuwar hanya ana ɗaukar gado. Babban sashin TPL shine ajin Aiki daga System.Threading.Tasks namespace. Aiki shine abstraction akan zaren. Tare da sabon sigar yaren C #, mun sami kyakkyawar hanya don aiki tare da Ayyuka - async/wait operators. Wadannan ra'ayoyin sun ba da damar rubuta lambar asynchronous kamar dai mai sauƙi ne kuma mai aiki tare, wannan ya sa har ma ga mutanen da ba su da ƙananan fahimtar ayyukan ciki na zaren rubutu su iya rubuta aikace-aikacen da ke amfani da su, aikace-aikacen da ba su daskare lokacin yin aiki mai tsawo. Yin amfani da async/jiran jigo ne na ɗaya ko ma labarai da yawa, amma zan yi ƙoƙarin samun ainihin shi a cikin ƴan jimloli:

  • async shine mai gyara hanyar dawo da Aiki ko wofi
  • kuma jira mai aiki ne mara toshewa.

Har yanzu: ma'aikacin jira, a cikin yanayin gabaɗaya (akwai keɓancewa), zai sake sakin zaren aiwatar da na yanzu, kuma lokacin da Task ɗin ya gama aiwatar da shi, da zaren (a zahiri, zai fi dacewa a faɗi mahallin. , amma ƙari akan hakan daga baya) zai ci gaba da aiwatar da hanyar gaba. A cikin NET, ana aiwatar da wannan hanyar kamar yadda aka dawo da amfanin gona, lokacin da hanyar da aka rubuta ta zama gabaɗayan aji, wanda na'ura ce ta jihar kuma ana iya aiwatar da ita ta daban-daban dangane da waɗannan jihohin. Duk mai sha'awar zai iya rubuta kowace lamba mai sauƙi ta amfani da asynс/jira, tattarawa da duba taron ta amfani da JetBrains dotPeek tare da Kunna Ƙwararren Ƙwararren Ƙwararren Ƙirar.

Bari mu kalli zaɓuɓɓuka don ƙaddamarwa da amfani da ɗawainiya. A cikin misalin lambar da ke ƙasa, mun ƙirƙiri sabon aiki wanda ba ya yin wani amfani (Zare.Barci(10000)), amma a rayuwa ta ainihi wannan ya kamata ya zama wasu hadadden aiki na CPU.

using TCO = System.Threading.Tasks.TaskCreationOptions;

public static async void VoidAsyncMethod() {
    var cancellationSource = new CancellationTokenSource();

    await Task.Factory.StartNew(
        // Code of action will be executed on other context
        () => Thread.Sleep(10000),
        cancellationSource.Token,
        TCO.LongRunning | TCO.AttachedToParent | TCO.PreferFairness,
        scheduler
    );

    //  Code after await will be executed on captured context
}

An ƙirƙiri ɗawainiya tare da zaɓuɓɓuka masu yawa:

  • LongRunning alama ce cewa ba za a kammala aikin da sauri ba, wanda ke nufin yana iya zama darajar yin la'akari da rashin ɗaukar zaren daga tafkin, amma ƙirƙirar wani dabam don wannan Aiki don kada ya cutar da wasu.
  • AttachedToParent - Ana iya tsara ayyuka a cikin matsayi. Idan an yi amfani da wannan zaɓi, to, aikin yana iya kasancewa a cikin yanayin da kanta ya ƙare kuma yana jiran a kashe 'ya'yansa.
  • PreferFairness - yana nufin cewa zai fi kyau a aiwatar da Ayyukan da aka aiko don aiwatarwa a baya kafin waɗanda aka aiko daga baya. Amma wannan shawara ce kawai kuma ba a tabbatar da sakamako ba.

Siga na biyu da aka wuce zuwa hanyar shine CancelationToken. Don sarrafa sokewar aiki daidai bayan an fara shi, lambar da ake aiwatarwa dole ne ta cika da cak na jihar CancellationToken. Idan babu cak, to hanyar Cancel da ake kira CancellationTokenSource abu zai iya dakatar da aiwatar da aikin kafin ya fara.

Siga na ƙarshe shine abu mai tsara jadawalin nau'in TaskScheduler. An tsara wannan ajin da zuriyarsa don sarrafa dabarun rarraba Ayyuka a cikin zaren; ta tsohuwa, aikin za a aiwatar da shi akan zaren bazuwar daga tafkin.

Ana amfani da ma'aikacin jiran aiki a kan Task ɗin da aka ƙirƙira, wanda ke nufin lambar da aka rubuta bayanta, idan akwai ɗaya, za a aiwatar da ita a cikin mahallin guda ɗaya (sau da yawa wannan yana nufin akan zaren iri ɗaya) kamar lambar kafin jira.

Hanyar ana yiwa alama alama a matsayin async void, wanda ke nufin zai iya amfani da mai jiran aiki, amma lambar kiran ba za ta iya jira don aiwatarwa ba. Idan irin wannan fasalin ya zama dole, to dole ne hanyar dawo da Aiki. Hanyoyin da aka yiwa alama async banza suna da yawa: a matsayin mai mulkin, waɗannan su ne masu gudanar da taron ko wasu hanyoyin da ke aiki akan wuta da manta ka'ida. Idan kana buƙatar ba kawai damar ba da damar jira har zuwa ƙarshen kisa, amma kuma mayar da sakamakon, to, kana buƙatar amfani da Task.

A kan Task ɗin da hanyar StartNew ta dawo, da kuma kowane ɗayan, zaku iya kiran hanyar ConfigureAwait tare da ma'aunin ƙarya, sannan aiwatarwa bayan jira ba zai ci gaba ba akan mahallin da aka kama, amma akan sabani. Ya kamata a yi wannan koyaushe lokacin da mahallin aiwatarwa ba shi da mahimmanci ga lambar bayan jira. Wannan kuma shawara ce daga MS lokacin rubuta lambar da za a kawo cikin kunshin a ɗakin karatu.

Bari mu ɗan ɗan dakata kan yadda za ku iya jira kammala ɗawainiya. A ƙasa akwai misali na lamba, tare da sharhi kan lokacin da ake tsammanin yana da kyau da kuma lokacin da aka yi shi mara kyau.

public static async void AnotherMethod() {

    int result = await AsyncMethod(); // good

    result = AsyncMethod().Result; // bad

    AsyncMethod().Wait(); // bad

    IEnumerable<Task> tasks = new Task[] {
        AsyncMethod(), OtherAsyncMethod()
    };

    await Task.WhenAll(tasks); // good
    await Task.WhenAny(tasks); // good

    Task.WaitAll(tasks.ToArray()); // bad
}

A misali na farko, muna jira Task ɗin ya ƙare ba tare da toshe zaren kira ba, za mu dawo don sarrafa sakamakon ne kawai lokacin da ya riga ya kasance, har zuwa lokacin, zaren kiran yana barin abin da ya dace.

A cikin zaɓi na biyu, muna toshe zaren kira har sai an ƙididdige sakamakon hanyar. Wannan ba daidai ba ne kawai saboda mun shagaltar da zaren, irin wannan albarkatu mai mahimmanci na shirin, tare da sauƙi mai sauƙi, amma kuma saboda idan lambar hanyar da muke kira ta ƙunshi jira, kuma mahallin daidaitawa yana buƙatar komawa zuwa zaren kira bayan haka. jira, to, za mu sami matsi: Zaren kira yana jiran sakamakon asynchronous hanyar da za a ƙididdige shi, hanyar asynchronous yana ƙoƙari a banza don ci gaba da aiwatar da shi a cikin zaren kira.

Wani rashin lahani na wannan hanya shine rikitattun kurakurai. Gaskiyar ita ce kurakurai a cikin lambar asynchronous lokacin amfani da async / jira suna da sauƙin ɗauka - suna yin daidai da idan lambar ta daidaita. Yayin da idan muka yi amfani da tsattsauran ra'ayi na aiki tare zuwa ɗawainiya, keɓantawar asali ta juya zuwa AggregateException, i.e. Don kula da keɓancewar, dole ne ku bincika nau'in InnerException kuma ku rubuta sarkar da kanku a cikin shingen kamawa ɗaya ko amfani da kama lokacin da aka gina, maimakon sarƙar kama tubalan da aka fi sani a cikin C # duniya.

Misalai na uku da na ƙarshe kuma ana yiwa alama mara kyau saboda dalili ɗaya kuma sun ƙunshi duk matsaloli iri ɗaya.

The WhenAny and WhenAllHanyoyin suna da matuƙar dacewa don jiran rukunin Ayyuka; suna haɗa rukuni na Ayyuka zuwa ɗaya, wanda zai kunna ko dai lokacin da aka fara kunna ɗawainiya daga ƙungiyar, ko kuma lokacin da dukkansu suka gama aiwatar da su.

Tsayawa zaren

Don dalilai daban-daban, yana iya zama dole a dakatar da kwarara bayan ya fara. Akwai hanyoyi da yawa don yin wannan. Ajin Thread yana da hanyoyi guda biyu da suka dace: Zubar da ciki и katse. Na farko ba a ba da shawarar yin amfani da shi ba, saboda bayan kiran shi a kowane lokaci bazuwar, yayin aiwatar da kowane umarni, za a jefa banda ThreadAbortedException. Ba ku tsammanin za a jefar da irin wannan keɓancewar yayin ƙara kowane ma'aunin lamba, daidai? Kuma lokacin amfani da wannan hanya, wannan lamari ne na gaske. Idan kuna buƙatar hana CLR ƙirƙirar irin wannan keɓantawa a cikin wani yanki na lamba, zaku iya kunsa shi cikin kira. Zare.FarkonCriticalYanki, Zare.KarshenCriticalYanki. Duk wani lamba da aka rubuta a cikin toshewar ƙarshe ana naɗe shi cikin irin waɗannan kiran. Saboda wannan dalili, a cikin zurfin tsarin tsarin za ku iya samun tubalan tare da gwadawa mara kyau, amma ba komai ba a ƙarshe. Microsoft yana hana wannan hanyar sosai ta yadda ba su haɗa ta a cikin .net core ba.

Hanyar Katsewa tana aiki da tsinkaya. Yana iya katse zaren ba tare da togiya ba ThreadInterruptedException kawai a lokacin da zaren yana cikin yanayin jira. Yana shiga wannan yanayin yayin da yake rataye yayin jiran WaitHandle, kulle, ko bayan kiran Thread.Sleep.

Duk zaɓuɓɓukan da aka bayyana a sama ba su da kyau saboda rashin tsinkayar su. Maganin shine a yi amfani da tsari CancellationToken da class CancellationTokenSource. Maganar ita ce: an ƙirƙiri misali na ajin CancellationTokenSource kuma wanda ya mallaki shi kaɗai zai iya dakatar da aikin ta hanyar kiran hanyar. soke. CancellationToken kawai ake wucewa zuwa aikin kanta. Masu CancelationToken ba za su iya soke aikin da kansu ba, amma kawai za su iya bincika ko an soke aikin. Akwai mallakar Boolean don wannan An Bukaci sokewa da hanya JifaIdanCancelAn Nemi. Na karshen zai jefa banda TaskCancelledException idan an kira hanyar Cancel akan misalin CancellationToken ana ɓarna. Kuma wannan ita ce hanyar da nake ba da shawarar amfani da ita. Wannan haɓakawa ne akan zaɓuɓɓukan da suka gabata ta hanyar samun cikakken iko akan lokacin da za'a iya soke aikin keɓantacce.

Mafi kyawun zaɓi don dakatar da zaren shine a kira aikin Win32 API TerminateThread. Halin CLR bayan kiran wannan aikin na iya zama mara tabbas. A kan MSDN an rubuta waɗannan abubuwa game da wannan aikin: “TerminateThread aiki ne mai haɗari wanda yakamata a yi amfani da shi kawai a cikin matsanancin yanayi. "

Mayar da gadon API zuwa Taskar Aiki ta amfani da hanyar FromAsync

Idan kun yi sa'a don yin aiki a kan aikin da aka fara bayan ƙaddamar da Ayyuka kuma an daina haifar da tsoro ga yawancin masu haɓakawa, to ba za ku yi hulɗa da tsofaffin APIs masu yawa ba, na ɓangare na uku da waɗanda ƙungiyar ku. ya azabtar da su a baya. An yi sa'a, ƙungiyar NET Framework ta kula da mu, ko da yake watakila manufar ita ce mu kula da kanmu. Ko ta yaya, NET yana da kayan aiki da yawa don canza lambar ba tare da ɓaci ba da aka rubuta a cikin tsoffin hanyoyin shirye-shiryen asynchronous zuwa sabuwar. Ɗayan su shine hanyar FromAsync na TaskFactory. A cikin misalin lambar da ke ƙasa, na kunsa tsoffin hanyoyin async na rukunin WebRequest a cikin ɗawainiya ta amfani da wannan hanyar.

object state = null;
WebRequest wr = WebRequest.CreateHttp("http://github.com");
await Task.Factory.FromAsync(
    wr.BeginGetResponse,
    we.EndGetResponse
);

Wannan misali ne kawai kuma ba za ku iya yin wannan tare da nau'ikan ginannun ba, amma duk wani tsohon aikin yana cike da hanyoyin BeginDoSomething da ke dawo da IAsyncResult da EndDoSomething hanyoyin da suka karɓa.

Canza API na gado zuwa Aiki bisa Aiki ta amfani da ajin TaskCompletionSource

Wani muhimmin kayan aiki don la'akari shine aji TaskCompletionSource. Dangane da ayyuka, manufa da ƙa'idar aiki, yana iya zama ɗan tuno da hanyar RegisterWaitForSingleObject na ajin ThreadPool, wanda na rubuta game da shi a sama. Yin amfani da wannan ajin, zaku iya naɗa tsoffin API ɗin asynchronous cikin sauƙi da dacewa a cikin Ayyuka.

Za ku ce na riga na yi magana game da hanyar FromAsync na ajin TaskFactory da aka yi niyya don waɗannan dalilai. Anan dole ne mu tuna da tarihin ci gaban samfuran asynchronous a cikin .net wanda Microsoft ya ba da a cikin shekaru 15 da suka gabata: kafin Tsarin Asynchronous Asynchronous (TAP), akwai Tsarin Asynchronous Programming (APP), wanda ya game hanyoyin faraWani abu yana dawowa Sakamakon IAsync da hanyoyin karshenWani abu da ya yarda da shi kuma don gadon waɗannan shekarun hanyar FromAsync cikakke ne, amma bayan lokaci, an maye gurbin shi da Tsarin Asynchronous Asynchronous ( Event Based Asynchronous Pattern )EAP), wanda ya ɗauka cewa za a tada wani taron idan an kammala aikin asynchronous.

TaskCompletionSource cikakke ne don naɗa Ayyuka da APIs na gado waɗanda aka gina a kusa da ƙirar taron. Ma'anar aikinsa shine kamar haka: wani abu na wannan aji yana da dukiyar jama'a na nau'in Task, yanayin da za'a iya sarrafa shi ta hanyar SetResult, SetException, da dai sauransu hanyoyin TaskCompletionSource class. A wuraren da aka yi amfani da ma'aikacin jira zuwa wannan Aiki, za'a aiwatar da shi ko gazawa tare da keɓancewa dangane da hanyar da aka yi amfani da TaskCompletionSource. Idan har yanzu ba a fayyace ba, bari mu kalli wannan misalin lambar, inda aka nannade wasu tsoffin EAP API a cikin ɗawainiya ta amfani da TaskCompletionSource: lokacin da taron ya tashi, za a sanya aikin a cikin Kammala jihar, da kuma hanyar da ta yi amfani da ma'aikacin jira. zuwa wannan Aikin zai ci gaba da aiwatar da shi bayan an karɓi abin sakamakon.

public static Task<Result> DoAsync(this SomeApiInstance someApiObj) {

    var completionSource = new TaskCompletionSource<Result>();
    someApiObj.Done += 
        result => completionSource.SetResult(result);
    someApiObj.Do();

    result completionSource.Task;
}

TaskCompletionSource Tukwici & Dabaru

Rufe tsoffin APIs ba shine abin da za a iya yi ta amfani da TaskCompletionSource ba. Yin amfani da wannan ajin yana buɗe yuwuwar ƙirƙira APIs daban-daban akan Ayyukan da ba a shagaltar da su ta hanyar zaren. Kuma rafi, kamar yadda muke tunawa, albarkatu ne mai tsada kuma adadin su yana iyakance (mafi yawan adadin RAM). Ana iya samun wannan ƙayyadaddun cikin sauƙi ta haɓaka, misali, aikace-aikacen gidan yanar gizo da aka ɗora tare da hadaddun dabarun kasuwanci. Bari mu yi la'akari da yiwuwar da nake magana game da su lokacin aiwatar da irin wannan dabara kamar Dogon Zabe.

A takaice dai, ainihin dabarar ita ce: kuna buƙatar karɓar bayanai daga API game da wasu abubuwan da ke faruwa a gefensa, yayin da API, saboda wasu dalilai, ba zai iya ba da rahoton lamarin ba, amma kawai zai iya dawo da jihar. Misalin waɗannan su ne duk APIs da aka gina a saman HTTP kafin lokutan WebSocket ko lokacin da ba zai yiwu ba saboda wasu dalilai don amfani da wannan fasaha. Abokin ciniki na iya tambayar uwar garken HTTP. Sabar HTTP ba zata iya fara sadarwa tare da abokin ciniki ba. Magani mai sauƙi shine yin zaɓen uwar garken ta amfani da mai ƙidayar lokaci, amma wannan yana haifar da ƙarin kaya akan uwar garken da ƙarin jinkiri akan matsakaicin TimerInterval / 2. Don samun kewaye da wannan, an ƙirƙiri wata dabara mai suna Long Polling, wanda ya haɗa da jinkirta amsa daga uwar garken har sai Timeout ya ƙare ko wani abu zai faru. Idan lamarin ya faru, to ana sarrafa shi, idan ba haka ba, sai a sake aika buƙatar.

while(!eventOccures && !timeoutExceeded)  {

  CheckTimout();
  CheckEvent();
  Thread.Sleep(1);
}

Amma irin wannan maganin zai tabbatar da zama mai muni da zaran yawan abokan ciniki da ke jiran taron ya karu, saboda ... Kowane irin wannan abokin ciniki ya mamaye gabaɗayan zaren jiran wani taron. Ee, kuma muna samun ƙarin jinkiri na 1ms lokacin da abin ya faru, galibi wannan ba shi da mahimmanci, amma me yasa software ta yi muni fiye da yadda zata iya zama? Idan muka cire Thread.Sleep(1), to a banza za mu loda processor core 100% mara amfani, yana juyawa a cikin sake zagayowar mara amfani. Amfani da TaskCompletionSource zaka iya sake yin wannan lambar cikin sauƙi kuma ka warware duk matsalolin da aka gano a sama:

class LongPollingApi {

    private Dictionary<int, TaskCompletionSource<Msg>> tasks;

    public async Task<Msg> AcceptMessageAsync(int userId, int duration) {

        var cs = new TaskCompletionSource<Msg>();
        tasks[userId] = cs;
        await Task.WhenAny(Task.Delay(duration), cs.Task);
        return cs.Task.IsCompleted ? cs.Task.Result : null;
    }

    public void SendMessage(int userId, Msg m) {

        if (tasks.TryGetValue(userId, out var completionSource))
            completionSource.SetResult(m);
    }
}

Wannan lambar ba a shirye take ba, amma demo kawai. Don amfani da shi a lokuta na ainihi, kuna buƙatar, aƙalla, don kula da yanayin lokacin da saƙo ya zo a lokacin da babu wanda ke jiransa: a wannan yanayin, hanyar AsseptMessageAsync yakamata ya dawo da aikin da aka rigaya ya gama. Idan wannan shine lamarin da ya fi kowa, to zaku iya tunanin yin amfani da ValueTask.

Lokacin da muka karɓi buƙatun saƙo, muna ƙirƙira kuma mu sanya TaskCompletionSource a cikin ƙamus, sannan jira abin da zai faru da farko: ƙayyadadden tazarar lokaci ya ƙare ko an karɓi saƙo.

ValueTask: me yasa kuma ta yaya

Masu aiki na async / jiran aiki, kamar mai ba da amsawar yawan amfanin ƙasa, suna samar da injin jiha daga hanyar, kuma wannan shine ƙirƙirar sabon abu, wanda kusan koyaushe ba shi da mahimmanci, amma a lokuta da yawa yana iya haifar da matsala. Wannan shari'ar na iya zama hanyar da ake kira da gaske sau da yawa, muna magana ne game da dubun da ɗaruruwan dubban kira a cikin dakika ɗaya. Idan irin wannan hanyar an rubuta ta hanyar da a mafi yawan lokuta ta dawo da sakamako ta ƙetare duk hanyoyin jira, to .NET yana ba da kayan aiki don inganta wannan - tsarin ValueTask. Don bayyana shi, bari mu dubi misalin amfani da shi: akwai cache da muke zuwa sau da yawa. Akwai wasu dabi'u a ciki sannan mu mayar da su kawai; idan ba haka ba, sai mu je wasu jinkirin IO don samun su. Ina so in yi na ƙarshen asynchronously, wanda ke nufin gabaɗayan hanyar ta zama asynchronous. Don haka, zahirin hanyar rubuta hanyar ita ce kamar haka:

public async Task<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return val;
    return await RequestById(id);
}

Saboda sha'awar ingantawa kaɗan, da kuma ɗan jin tsoron abin da Roslyn zai haifar yayin haɗa wannan lambar, zaku iya sake rubuta wannan misali kamar haka:

public Task<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return Task.FromResult(val);
    return RequestById(id);
}

Lallai, mafi kyawun mafita a cikin wannan yanayin shine haɓaka hanyar zafi, wato, samun ƙima daga ƙamus ba tare da wani ƙasƙanci mara amfani da kaya akan GC ba, yayin da a cikin waɗancan lokuta da ba kasafai muke buƙatar zuwa IO don bayanai ba. , komai zai kasance ƙari/rasa tsohuwar hanya:

public ValueTask<string> GetById(int id) {

    if (cache.TryGetValue(id, out string val))
        return new ValueTask<string>(val);
    return new ValueTask<string>(RequestById(id));
}

Bari mu dubi wannan yanki na lambar: idan akwai darajar a cikin cache, muna ƙirƙirar tsari, in ba haka ba za a nannade ainihin aikin a cikin ma'ana. Lambar kiran ba ta damu da wace hanya aka aiwatar da wannan lambar a cikin: ValueTask, daga mahallin ma'anar ma'anar C #, za ta yi aiki iri ɗaya da ɗawainiya na yau da kullun a wannan yanayin.

TaskSchedulers: sarrafa dabarun ƙaddamar da ayyuka

API na gaba wanda zan so in yi la'akari shine aji Jadawalin Aiki da abubuwan da suka samo asali. Na riga na ambata a sama cewa TPL yana da ikon sarrafa dabaru don rarraba Ayyuka a cikin zaren. Irin waɗannan dabarun an bayyana su a cikin zuriyar TaskScheduler. Kusan kowace dabara da kuke buƙata ana iya samuwa a cikin ɗakin karatu. ParallelExtensionsExtras, Microsoft ya haɓaka, amma ba ɓangare na .NET ba, amma ana kawo shi azaman kunshin Nuget. Bari mu dubi wasu daga cikinsu a takaice:

  • JadawalinTaskarTaɗi na Yanzu - yana aiwatar da ayyuka akan zaren na yanzu
  • Ƙarƙashin Ƙarƙashin Ƙarƙashin Ƙarƙashin Ƙarƙashin Ƙarƙashin Ƙaƙwalwa - yana iyakance adadin Ayyukan da aka aiwatar a lokaci guda ta hanyar siga N, wanda aka karɓa a cikin maginin ginin.
  • Jadawalin aikin da aka ba da oda - an bayyana shi azaman LimitedConcurrencyLevelTaskScheduler(1), don haka za a aiwatar da ayyuka a jere.
  • Jadawalin Satar Aiki - kayan aiki satar aiki tsarin kula da rarraba ayyuka. Mahimmanci shine keɓantaccen Wurin Wuta. Yana magance matsalar cewa a cikin NET ThreadPool aji ne a tsaye, ɗaya ga duk aikace-aikacen, wanda ke nufin cewa yin amfani da shi ba daidai ba a wani ɓangaren shirin na iya haifar da lahani a wani. Bugu da ƙari, yana da matuƙar wahala a fahimci dalilin irin wannan lahani. Wannan. Ana iya samun buƙatar amfani daban-daban WorkStealingTaskSchedulers a cikin sassan shirin inda amfani da ThreadPool na iya zama m da rashin tabbas.
  • TakaddamaTaskScheduler - yana ba ku damar yin ayyuka bisa ga ka'idodin layin fifiko
  • Tsare-tsarePerTaskScheduler - yana ƙirƙirar zare daban don kowane ɗawainiya da aka aiwatar akansa. Zai iya zama da amfani ga ayyukan da ke ɗaukar lokaci mai tsawo mara tsinkaya don kammalawa.

Akwai mai kyau daki-daki labarin game da TaskSchedulers akan bulogin Microsoft.

Don dacewa da gyara duk abin da ke da alaƙa da Ayyuka, Kayayyakin Kayayyakin Kayayyakin Yana da taga Tasks. A cikin wannan taga zaku iya ganin yanayin aikin na yanzu kuma ku tsallake zuwa layin da ake aiwatarwa a halin yanzu.

NET: Kayan aiki don aiki tare da multithreading da asynchony. Kashi na 1

PLinq da Parallel class

Baya ga Ayyuka da duk abin da aka faɗa game da su, akwai ƙarin kayan aiki masu ban sha'awa guda biyu a cikin NET: PLinq (Linq2Parallel) da Parallel class. Na farko yayi alƙawarin yin daidai da aiwatar da duk ayyukan Linq akan zaren da yawa. Ana iya saita adadin zaren ta amfani da hanyar tsawaita WithDegreeOfParallelism. Abin takaici, mafi yawan lokuta PLinq a cikin yanayin da ya dace ba shi da isasshen bayani game da abubuwan ciki na tushen bayanan ku don samar da riba mai mahimmanci, a gefe guda, farashin ƙoƙari ya yi ƙasa sosai: kawai kuna buƙatar kiran hanyar AsParallel kafin. sarkar hanyoyin Linq da gudanar da gwaje-gwajen aiki. Bugu da ƙari, yana yiwuwa a ba da ƙarin bayani ga PLinq game da yanayin tushen bayanan ku ta amfani da tsarin Partitions. Kuna iya karantawa a nan и a nan.

Parallel static class yana ba da hanyoyi don maimaitawa ta hanyar tarin Foreach a layi daya, aiwatar da madaukai, da aiwatar da wakilai da yawa a cikin layi daya. Za a daina aiwatar da zaren yanzu har sai an kammala lissafin. Ana iya saita adadin zaren ta hanyar wucewa ParallelOptions azaman hujja ta ƙarshe. Hakanan zaka iya saka TaskScheduler da CancellationToken ta amfani da zaɓuɓɓuka.

binciken

Lokacin da na fara rubuta wannan labarin bisa ga kayan aikin rahotona da kuma bayanan da na tattara a lokacin aikina bayansa, ban yi tsammanin za a yi yawa ba. Yanzu, lokacin da editan rubutun da nake buga wannan labarin cikin wulakanci ya gaya mani cewa shafi na 15 ya tafi, zan taƙaita sakamakon wucin gadi. Sauran dabaru, APIs, kayan aikin gani da ramuka za a rufe su a cikin labarin na gaba.

Ƙarshe:

  • Kuna buƙatar sanin kayan aikin don aiki tare da zaren, asynchony da layi daya don amfani da albarkatun PC na zamani.
  • NET yana da kayan aiki daban-daban don waɗannan dalilai
  • Ba duka ba ne suka bayyana a lokaci ɗaya, don haka sau da yawa zaka iya samun na gado, duk da haka, akwai hanyoyin canza tsoffin APIs ba tare da ƙoƙari sosai ba.
  • Yin aiki tare da zaren a cikin NET ana wakilta ta azuzuwan Zaure da ThreadPool
  • The Thread.Abort, Thread.Interrupt, da Win32 API Kashe hanyoyin zare suna da haɗari kuma ba a ba da shawarar yin amfani da su ba. Madadin haka, yana da kyau a yi amfani da hanyar CancellationToken
  • Gudun ruwa abu ne mai mahimmanci kuma wadatar sa yana da iyaka. Yakamata a guji yanayin da zaren ke shagaltu da jiran abubuwan da suka faru. Don wannan ya dace don amfani da ajin TaskCompletionSource
  • Mafi ƙarfi da haɓaka kayan aikin NET don aiki tare da daidaitawa da asynchony sune Ayyuka.
  • Masu aiki na c # async/wait suna aiwatar da manufar rashin toshewa jira
  • Kuna iya sarrafa rarraba ayyuka a cikin zaren ta amfani da azuzuwan da aka samo TaskScheduler
  • Tsarin ValueTask na iya zama da amfani wajen inganta hanyoyin zafi da zirga-zirgar ƙwaƙwalwa
  • Ayyuka na Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Kayayyakin Wuta)
  • PLinq kayan aiki ne mai kyau, amma ƙila ba shi da isasshen bayani game da tushen bayanan ku, amma ana iya gyara wannan ta amfani da tsarin rarrabawa.
  • A ci gaba…

source: www.habr.com

Add a comment