.NET: Mga himan alang sa pagtrabaho sa multithreading ug asynchrony. Bahin 1

Akong gipatik ang orihinal nga artikulo sa Habr, ang hubad niini gi-post sa korporasyon post sa blog.

Ang panginahanglan sa pagbuhat sa usa ka butang nga asynchronously, nga walay paghulat alang sa resulta dinhi ug karon, o sa pagbahin sa dako nga buhat sa taliwala sa pipila ka mga yunit sa pagbuhat niini, naglungtad sa wala pa ang pag-abut sa mga computer. Sa ilang pag-abot, kini nga panginahanglan nahimong mahikap kaayo. Karon, sa 2019, gi-type nako kini nga artikulo sa usa ka laptop nga adunay 8-core Intel Core processor, diin kapin sa usa ka gatos nga mga proseso ang nagdagan nga managsama, ug labi pa nga mga hilo. Sa duol, adunay usa ka gamay nga shabby nga telepono, gipalit pipila ka tuig na ang milabay, kini adunay usa ka 8-core processor nga sakay. Ang mga kapanguhaan sa tema puno sa mga artikulo ug mga video diin ang ilang mga tagsulat nakadayeg sa mga punoan nga smartphone karong tuiga nga adunay 16-core nga mga processor. Naghatag ang MS Azure og virtual machine nga adunay 20 core processor ug 128 TB RAM nga ubos sa $2/hour. Ikasubo, imposible nga makuha ang labing kadaghan ug magamit kini nga gahum nga dili madumala ang interaksyon sa mga hilo.

Terminolohiya

Proseso - OS object, nahilit nga address space, adunay mga thread.
Thread - usa ka butang sa OS, ang pinakagamay nga yunit sa pagpatuman, bahin sa usa ka proseso, ang mga hilo nagpaambit sa memorya ug uban pang mga kahinguhaan sa ilang kaugalingon sulod sa usa ka proseso.
Multitasking - OS property, ang abilidad sa pagpadagan sa daghang mga proseso nga dungan
Multi-core - usa ka kabtangan sa processor, ang abilidad sa paggamit sa daghang mga cores alang sa pagproseso sa datos
Multiprocessing - usa ka kabtangan sa usa ka kompyuter, ang abilidad sa dungan nga pagtrabaho sa daghang mga processor sa pisikal
Multithreading — usa ka kabtangan sa usa ka proseso, ang abilidad sa pag-apod-apod sa pagproseso sa datos taliwala sa daghang mga hilo.
Paralelismo - paghimo sa daghang mga aksyon nga pisikal nga dungan matag yunit sa oras
Asynchrony - pagpatuman sa usa ka operasyon nga wala maghulat sa pagkompleto niini nga pagproseso; ang resulta sa pagpatay mahimong maproseso sa ulahi.

Metapora

Dili tanan nga mga kahulugan maayo ug ang uban nanginahanglan dugang nga katin-awan, mao nga magdugang ako usa ka metapora bahin sa pagluto sa pamahaw sa pormal nga gipaila nga terminolohiya. Ang pagluto og pamahaw niini nga metapora usa ka proseso.

Samtang nag-andam og pamahaw sa buntag ako (CPU) adto ko sa kusina (Computer). naa koy 2 ka kamot(Cores). Adunay daghang mga gamit sa kusina (IO): hurnohan, kettle, toaster, refrigerator. Akong i-on ang gas, butangan ug frying pan ug buboan ug mantika nga dili na maghulat nga moinit (asynchronously, Non-Blocking-IO-Wait), Gikuha nako ang mga itlog gikan sa refrigerator ug gibuak kini sa usa ka plato, dayon gibunalan kini sa usa ka kamot (Thread #1), ug ikaduha (Thread #2) nga nagkupot sa plato (Shared Resource). Karon gusto nako nga i-on ang kettle, apan wala koy igong mga kamot (Kagutom sa Thread) Niining panahona, ang frying pan nag-init (Pagproseso sa resulta) diin akong gibubo ang akong gibunalan. Gikuha nako ang kettle ug gipasiga kini ug binuang nga nagtan-aw sa tubig nga nagbukal niini (Pag-block-IO-Paghulat), bisan tuod niining panahona mahimo niyang hugasan ang plato diin iyang gilatigo ang torta.

Nagluto ko og omelette gamit lang ang 2 ka kamot, ug wala na ko, apan sa samang higayon, sa higayon sa paglatigo sa omelette, 3 ka mga operasyon ang nahitabo sa usa ka higayon: paglatigo sa omelette, paghawid sa plato, pagpainit sa frying pan. Ang CPU mao ang pinakapaspas nga bahin sa kompyuter, ang IO mao ang kasagaran nga mohinay ang tanan, mao nga kasagaran usa ka epektibong solusyon mao ang pag-okupar sa CPU sa usa ka butang samtang nagdawat ug data gikan sa IO.

Pagpadayon sa metapora:

  • Kung sa proseso sa pag-andam sa usa ka omelet, sulayan usab nako ang pag-ilis sa mga sinina, kini usa ka pananglitan sa multitasking. Usa ka importante nga nuance: ang mga kompyuter mas maayo niini kay sa mga tawo.
  • Usa ka kusina nga adunay daghang mga chef, pananglitan sa usa ka restawran - usa ka multi-core nga kompyuter.
  • Daghang mga restawran sa usa ka food court sa usa ka shopping center - data center

.NET Tools

Ang .NET maayo sa pagtrabaho sa mga hilo, sama sa uban pang mga butang. Sa matag bag-ong bersyon, kini nagpaila sa dugang ug mas bag-ong mga himan alang sa pagtrabaho uban kanila, bag-ong mga layer sa abstraction sa OS thread. Kung nagtrabaho kauban ang pagtukod sa mga abstraction, ang mga nag-develop sa framework naggamit sa usa ka pamaagi nga nagbilin sa oportunidad, kung gigamit ang usa ka taas nga lebel nga abstraction, sa pagpaubos sa usa o daghang lebel sa ubos. Kasagaran kini dili kinahanglan, sa pagkatinuod kini nag-abli sa pultahan sa pagpusil sa imong kaugalingon sa tiil sa usa ka shotgun, apan usahay, sa talagsaon nga mga kaso, kini mahimo nga ang bugtong paagi sa pagsulbad sa usa ka problema nga wala masulbad sa kasamtangan nga lebel sa abstraction. .

Pinaagi sa mga himan, gipasabut nako ang duha nga mga interface sa pagprograma sa aplikasyon (API) nga gihatag sa balangkas ug mga pakete sa ikatulo nga partido, ingon man ang tibuuk nga mga solusyon sa software nga nagpasimple sa pagpangita sa bisan unsang mga problema nga may kalabotan sa multi-threaded code.

Pagsugod ug thread

Ang Thread nga klase mao ang pinaka-basic nga klase sa .NET para sa pagtrabaho sa mga thread. Gidawat sa constructor ang usa sa duha ka delegado:

  • ThreadStart - Walay mga parameter
  • ParametrizedThreadStart - nga adunay usa ka parameter sa tipo nga butang.

Ang delegado ipatuman sa bag-ong binuhat nga thread human sa pagtawag sa Start method. Kung ang usa ka delegado sa tipo nga ParametrizedThreadStart gipasa ngadto sa constructor, nan ang usa ka butang kinahanglang ipasa ngadto sa Start method. Kini nga mekanismo gikinahanglan aron mabalhin ang bisan unsang lokal nga impormasyon sa sapa. Angay nga matikdan nga ang paghimo sa usa ka hilo usa ka mahal nga operasyon, ug ang hilo mismo usa ka bug-at nga butang, labing menos tungod kay kini naggahin sa 1MB nga panumduman sa stack ug nanginahanglan pakig-uban sa OS API.

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

Ang ThreadPool nga klase nagrepresentar sa konsepto sa usa ka pool. Sa .NET, ang thread pool usa ka piraso sa engineering, ug ang mga developers sa Microsoft nagbutang ug daghang paningkamot sa pagsiguro nga kini molihok nga maayo sa lain-laing mga senaryo.

Kinatibuk-ang konsepto:

Gikan sa pagsugod sa aplikasyon, nagmugna kini daghang mga hilo nga gireserba sa background ug naghatag kaarang nga makuha kini aron magamit. Kung ang mga hilo gigamit kanunay ug sa daghang gidaghanon, ang pool molapad aron matubag ang mga panginahanglanon sa nagtawag. Kung wala’y libre nga mga hilo sa pool sa husto nga oras, maghulat kini nga mobalik ang usa sa mga hilo, o maghimo usa ka bag-o. Gisundan niini nga ang thread pool maayo alang sa pipila ka mga short-term nga aksyon ug dili maayo nga haum alang sa mga operasyon nga nagdagan isip mga serbisyo sa tibuok nga operasyon sa aplikasyon.

Aron magamit ang usa ka hilo gikan sa pool, adunay usa ka QueueUserWorkItem nga pamaagi nga modawat sa usa ka delegado sa tipo nga WaitCallback, nga adunay parehas nga pirma sa ParametrizedThreadStart, ug ang parameter nga gipasa niini naghimo sa parehas nga function.

ThreadPool.QueueUserWorkItem(...);

Ang dili kaayo nailhan nga thread pool nga pamaagi RegisterWaitForSingleObject gigamit sa pag-organisar sa dili pag-block sa mga operasyon sa IO. Ang delegado nga gipasa niini nga pamaagi tawgon kung ang WaitHandle nga gipasa sa pamaagi mao ang "Gipagawas".

ThreadPool.RegisterWaitForSingleObject(...)

Ang .NET adunay usa ka thread timer ug kini lahi sa WinForms/WPF timers kay ang handler niini tawgon sa usa ka thread nga gikuha gikan sa pool.

System.Threading.Timer

Adunay usab usa ka labi ka lahi nga paagi sa pagpadala usa ka delegado alang sa pagpatay sa usa ka hilo gikan sa pool - ang pamaagi sa BeginInvoke.

DelegateInstance.BeginInvoke

Gusto ko nga sa makadiyot magpuyo sa function diin daghan sa mga pamaagi sa ibabaw mahimong tawgon - CreateThread gikan sa Kernel32.dll Win32 API. Adunay usa ka paagi, salamat sa mekanismo sa mga pamaagi sa gawas, aron matawag kini nga function. Nakita nako ang ingon nga tawag kausa ra sa usa ka makalilisang nga pananglitan sa legacy code, ug ang pagdasig sa tagsulat nga nagbuhat sa eksakto niini nagpabilin nga usa ka misteryo alang kanako.

Kernel32.dll CreateThread

Pagtan-aw ug Pag-debug sa mga Thread

Ang mga thread nga imong gibuhat, ang tanang third-party nga mga component, ug ang .NET pool mahimong makita sa Threads window sa Visual Studio. Kini nga bintana magpakita lamang sa impormasyon sa thread kung ang aplikasyon ubos sa debug ug sa Break mode. Dinhi dali nimong makita ang mga ngalan sa stack ug mga prayoridad sa matag thread, ug ibalhin ang debugging sa usa ka piho nga hilo. Gamit ang Priority property sa Thread class, mahimo nimong itakda ang priority sa usa ka thread, nga malantaw sa OC ug CLR isip rekomendasyon sa pagbahin sa oras sa processor tali sa mga thread.

.NET: Mga himan alang sa pagtrabaho sa multithreading ug asynchrony. Bahin 1

Task Parallel Library

Ang Task Parallel Library (TPL) gipaila sa .NET 4.0. Karon kini ang sukaranan ug ang panguna nga himan alang sa pagtrabaho sa asynchrony. Ang bisan unsang kodigo nga naggamit ug daan nga pamaagi giisip nga kabilin. Ang batakang yunit sa TPL mao ang klase sa Task gikan sa System.Threading.Tasks namespace. Ang buluhaton usa ka abstraction sa usa ka hilo. Uban sa bag-ong bersyon sa C# nga lengguwahe, nakakuha kami usa ka matahum nga paagi sa pagtrabaho kauban ang Mga Buluhaton - async / naghulat nga mga operator. Kini nga mga konsepto nagpaposible sa pagsulat sa asynchronous code ingon nga kini yano ug dungan, kini nagpaposible bisan alang sa mga tawo nga adunay gamay nga pagsabut sa internal nga mga buhat sa mga hilo sa pagsulat sa mga aplikasyon nga naggamit niini, mga aplikasyon nga dili mag-freeze kung maghimo taas nga mga operasyon. Ang paggamit sa async/paghulat usa ka hilisgutan alang sa usa o bisan daghang mga artikulo, apan sulayan nako nga makuha ang kinatibuk-an niini sa pipila ka mga tudling-pulong:

  • Ang async usa ka modifier sa usa ka pamaagi nga nagbalik sa Task o wala
  • ug ang paghulat kay usa ka non-blocking Task waiting operator.

Sa makausa pa: ang naghulat nga operator, sa kinatibuk-ang kaso (adunay mga eksepsiyon), ipagawas ang kasamtangan nga hilo sa pagpatuman sa dugang, ug kung ang Task mahuman sa pagpatuman niini, ug ang hilo (sa pagkatinuod, mas husto nga isulti ang konteksto. , apan labaw pa niana sa ulahi) magpadayon sa pagpatuman sa pamaagi sa dugang. Sa sulod sa .NET, kini nga mekanismo gipatuman sa parehas nga paagi sama sa pagbalik sa ani, kung ang sinulat nga pamaagi nahimo nga usa ka tibuuk nga klase, nga usa ka makina sa estado ug mahimo’g ipatuman sa lainlaing mga bahin depende sa kini nga mga estado. Bisan kinsa nga interesado makasulat ug bisan unsang yano nga code gamit ang asynс/wait, pag-compile ug pagtan-aw sa asembliya gamit ang JetBrains dotPeek nga adunay Compiler Generated Code nga gipagana.

Atong tan-awon ang mga kapilian sa paglansad ug paggamit sa Task. Sa panig-ingnan sa code sa ubos, naghimo kami usa ka bag-ong buluhaton nga wala’y mahimo nga mapuslanon (Thread.Sleep(10000)), apan sa tinuud nga kinabuhi kini kinahanglan nga usa ka komplikado nga trabaho nga kusog sa 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
}

Ang usa ka buluhaton gihimo nga adunay daghang mga kapilian:

  • Ang LongRunning usa ka timaan nga ang buluhaton dili mahuman dayon, nga nagpasabut nga mahimo’g angay nga hunahunaon nga dili pagkuha usa ka hilo gikan sa pool, apan paghimo usa ka lahi alang niini nga Buluhaton aron dili makadaot sa uban.
  • AttachedToParent - Ang mga buluhaton mahimong gihan-ay sa usa ka hierarchy. Kung gigamit kini nga kapilian, nan ang Buluhaton mahimong naa sa usa ka estado diin kini mismo nahuman ug naghulat alang sa pagpatay sa mga anak niini.
  • PreferFairness - nagpasabot nga mas maayo nga ipatuman ang mga Buluhaton nga gipadala alang sa pagpatuman sa sayo pa sa dili pa ipadala sa ulahi. Apan kini usa lamang ka rekomendasyon ug ang mga resulta dili garantiya.

Ang ikaduha nga parameter nga gipasa sa pamaagi mao ang CancellationToken. Aron sa husto nga pagdumala sa pagkansela sa usa ka operasyon human kini magsugod, ang code nga gipatuman kinahanglan nga mapuno sa mga tseke alang sa CancellationToken nga estado. Kung walay mga tseke, nan ang Cancel nga pamaagi nga gitawag sa CancellationTokenSource object makapahunong sa pagpatuman sa Task lamang sa dili pa kini magsugod.

Ang katapusan nga parameter mao ang usa ka scheduler nga butang sa tipo nga TaskScheduler. Kini nga klase ug ang mga kaliwat niini gilaraw aron makontrol ang mga estratehiya sa pag-apod-apod sa mga Buluhaton sa mga thread; sa default, ang Buluhaton ipatuman sa usa ka random nga hilo gikan sa pool.

Ang operator nga naghulat gipadapat sa gibuhat nga Task, nga nagpasabut nga ang code nga gisulat pagkahuman niini, kung adunay usa, ipatuman sa parehas nga konteksto (kasagaran kini nagpasabut sa parehas nga hilo) ingon ang code sa wala pa maghulat.

Ang pamaagi gimarkahan nga async void, nga nagpasabut nga magamit ang operator nga naghulat, apan ang code sa pagtawag dili makahulat alang sa pagpatuman. Kung gikinahanglan ang ingon nga bahin, nan ang pamaagi kinahanglan nga ibalik ang Task. Ang mga pamaagi nga gimarkahan nga async void komon kaayo: kasagaran, kini ang mga tigdumala sa panghitabo o uban pang mga pamaagi nga molihok sa kalayo ug makalimot sa prinsipyo. Kung kinahanglan nimo nga dili lamang hatagan ang higayon nga maghulat hangtod sa katapusan sa pagpatay, apan ibalik usab ang resulta, nan kinahanglan nimo nga gamiton ang Task.

Sa Buluhaton nga ang StartNew nga pamaagi mibalik, ingon man sa bisan unsa nga lain, mahimo nimong tawagan ang ConfigureAwait nga pamaagi nga adunay sayup nga parameter, unya ang pagpatuman pagkahuman sa paghulat magpadayon dili sa nakuha nga konteksto, apan sa usa ka arbitraryo. Kinahanglan kini kanunay nga buhaton kung ang konteksto sa pagpatuman dili hinungdanon alang sa code pagkahuman naghulat. Kini usa usab ka rekomendasyon gikan sa MS sa pagsulat sa code nga ipadala nga giputos sa usa ka librarya.

Atong hisgotan og gamay kung unsaon nimo paghulat sa pagkompleto sa usa ka Buluhaton. Sa ubos usa ka pananglitan sa code, nga adunay mga komento kung kanus-a ang pagpaabut nahimo nga maayo ang kondisyon ug kung kini nahimo nga dili maayo nga kondisyon.

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
}

Sa una nga pananglitan, naghulat kami nga makompleto ang Buluhaton nga wala gibabagan ang hilo sa pagtawag; mobalik kami sa pagproseso sa resulta kung naa na kini; hangtod niana, ang hilo sa pagtawag nahabilin sa kaugalingon nga mga aparato.

Sa ikaduha nga kapilian, gibabagan namon ang hilo sa pagtawag hangtod makalkula ang sangputanan sa pamaagi. Kini daotan dili lamang tungod kay nag-okupar kami sa usa ka hilo, ingon usa ka bililhon nga kapanguhaan sa programa, nga adunay yano nga pagkatapulan, apan tungod usab kung ang code sa pamaagi nga among gitawag adunay naghulat, ug ang konteksto sa pag-synchronize nanginahanglan pagbalik sa hilo sa pagtawag pagkahuman. maghulat, unya makakuha kami usa ka deadlock : Ang calling thread naghulat alang sa resulta sa asynchronous nga pamaagi nga makalkula, ang asynchronous nga pamaagi naningkamot nga walay kapuslanan sa pagpadayon sa pagpatuman niini sa calling thread.

Ang laing disbentaha niini nga pamaagi mao ang komplikado nga pagdumala sa sayop. Ang kamatuoran mao nga ang mga sayop sa asynchronous code sa diha nga ang paggamit sa async/paghulat sayon ​​kaayo sa pagdumala - sila naggawi sa sama nga kon ang code mao ang synchronous. Samtang kung atong i-apply ang synchronous wait exorcism sa usa ka Task, ang orihinal nga eksepsiyon mahimong AggregateException, i.e. Aron madumala ang eksepsiyon, kinahanglan nimong susihon ang tipo sa InnerException ug isulat ang kon kadena sa imong kaugalingon sulod sa usa ka block block o gamita ang catch sa dihang magtukod, imbes nga kadena sa catch blocks nga mas pamilyar sa C# world.

Ang ikatulo ug katapusang mga pananglitan gimarkahan usab nga dili maayo alang sa parehas nga hinungdan ug adunay tanan nga parehas nga mga problema.

Ang WhenAny ug WhenAll nga mga pamaagi sayon ​​​​kaayo sa paghulat sa usa ka grupo sa mga Buluhaton; ilang giputos ang usa ka grupo sa mga Buluhaton ngadto sa usa, nga mobuto sa diha nga ang usa ka Buluhaton gikan sa grupo unang ma-trigger, o kung ang tanan niini nahuman sa ilang pagpatuman.

Paghunong sa mga hilo

Alang sa lainlaing mga hinungdan, mahimo’g kinahanglan nga hunongon ang pag-agos pagkahuman nagsugod. Adunay daghang mga paagi sa pagbuhat niini. Ang Thread nga klase adunay duha ka tukma nga ngalan nga mga pamaagi: Pag-abort и Makababag. Ang una dili kaayo girekomenda alang sa paggamit, tungod kay human sa pagtawag niini sa bisan unsa nga random nga higayon, sa panahon sa pagproseso sa bisan unsa nga instruksyon, usa ka eksepsiyon ang ilabay ThreadAbortedException. Wala ka magdahom nga ang ingon nga eksepsiyon ilabay kung magdugang sa bisan unsang integer variable, dili ba? Ug kung gamiton kini nga pamaagi, kini usa ka tinuod nga kahimtang. Kung kinahanglan nimo nga pugngan ang CLR sa paghimo sa ingon nga eksepsiyon sa usa ka piho nga seksyon sa code, mahimo nimo kini ibutang sa mga tawag Thread.BeginCriticalRegion, Thread.EndCriticalRegion. Ang bisan unsang code nga gisulat sa usa ka katapusan nga bloke giputos sa ingon nga mga tawag. Tungod niini nga hinungdan, sa kahiladman sa framework code makit-an nimo ang mga bloke nga adunay usa ka walay sulod nga pagsulay, apan dili usa ka walay sulod sa katapusan. Gi-discourage sa Microsoft kini nga pamaagi mao nga wala nila kini gilakip sa .net core.

Ang paagi sa Interrupt molihok nga mas matag-an. Mahimo kini makabalda sa hilo nga adunay eksepsiyon ThreadInterruptedException lamang sa mga higayon nga ang hilo anaa sa usa ka naghulat nga kahimtang. Kini mosulod niini nga kahimtang samtang nagbitay samtang naghulat sa WaitHandle, lock, o human sa pagtawag sa Thread.Sleep.

Ang duha ka mga kapilian nga gihulagway sa ibabaw dili maayo tungod sa ilang dili matag-an. Ang solusyon mao ang paggamit sa usa ka istruktura PagkanselaToken ug klase PagkanselaTokenSource. Ang punto mao kini: usa ka pananglitan sa klase sa CancellationTokenSource ang gihimo ug ang tag-iya ra ang makapahunong sa operasyon pinaagi sa pagtawag sa pamaagi. Cancel. Ang CancellationToken ra ang gipasa sa operasyon mismo. Ang mga tag-iya sa CancellationToken dili makakanselar sa operasyon sa ilang kaugalingon, apan mahimo ra nga susihon kung ang operasyon nakansela. Adunay usa ka Boolean nga kabtangan alang niini Gihangyo ang IsCancellation ug pamaagi Gihangyo ang ThrowIfCancel. Ang ulahi maghatag usa ka eksepsiyon TaskCancelledException kung ang Cancel nga pamaagi gitawag sa CancellationToken nga pananglitan nga gi-parroted. Ug kini ang pamaagi nga akong girekomenda nga gamiton. Kini usa ka pag-uswag sa miaging mga kapilian pinaagi sa pag-angkon sa hingpit nga kontrol sa kung unsang punto ang usa ka eksepsiyon nga operasyon mahimong ma-abort.

Ang labing bangis nga kapilian sa paghunong sa usa ka hilo mao ang pagtawag sa Win32 API TerminateThread function. Ang kinaiya sa CLR human sa pagtawag niini nga function mahimong dili matag-an. Sa MSDN ang mosunod gisulat mahitungod niini nga function: "Ang TerminateThread usa ka delikado nga function nga kinahanglan ra gamiton sa labing grabe nga mga kaso. “

Pag-convert sa legacy API ngadto sa Task Based gamit ang FromAsync nga pamaagi

Kung swerte ka nga nagtrabaho sa usa ka proyekto nga gisugdan pagkahuman gipaila ang Mga Buluhaton ug mihunong sa pagpahinabog hilom nga kalisang alang sa kadaghanan sa mga nag-develop, nan dili nimo kinahanglan nga atubangon ang daghang mga karaan nga mga API, parehas nga ikatulo nga partido ug kadtong imong team nagsakit kaniadto. Maayo na lang, ang .NET Framework team nag-atiman kanamo, bisan kung ang katuyoan mao ang pag-atiman sa among kaugalingon. Bisan pa, ang .NET adunay daghang mga himan alang sa walay sakit nga pag-usab sa code nga gisulat sa daan nga asynchronous nga mga pamaagi sa programming ngadto sa bag-o. Usa niini mao ang FromAsync nga pamaagi sa TaskFactory. Sa pananglitan sa code sa ubos, akong giputos ang daan nga async nga mga pamaagi sa WebRequest nga klase sa usa ka Task gamit kini nga pamaagi.

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

Kini usa lamang ka pananglitan ug dili nimo kinahanglan nga buhaton kini sa mga built-in nga tipo, apan ang bisan unsang karaan nga proyekto yano nga puno sa mga pamaagi sa BeginDoSomething nga nagbalik sa IAsyncResult ug EndDoSomething nga mga pamaagi nga nakadawat niini.

I-convert ang legacy API ngadto sa Task Based gamit ang TaskCompletionSource class

Ang laing importante nga himan nga tagdon mao ang klase Tinubdan sa Pagkompleto sa buluhaton. Sa termino sa mga gimbuhaton, katuyoan ug prinsipyo sa operasyon, kini mahimo nga medyo makapahinumdom sa RegisterWaitForSingleObject nga pamaagi sa ThreadPool nga klase, nga akong gisulat mahitungod sa ibabaw. Gamit kini nga klase, dali ug dali nimo maputos ang mga daan nga asynchronous nga API sa Mga Buluhaton.

Moingon ka nga nahisgotan ko na ang bahin sa FromAsync nga pamaagi sa klase sa TaskFactory nga gituyo alang niini nga mga katuyoan. Dinhi kinahanglan natong hinumdoman ang tibuok kasaysayan sa pagpalambo sa asynchronous nga mga modelo sa .net nga gitanyag sa Microsoft sa milabay nga 15 ka tuig: sa wala pa ang Task-Based Asynchronous Pattern (TAP), adunay Asynchronous Programming Pattern (APP), nga mahitungod sa mga pamaagi SugdiDoSomething nga nagbalik IAsyncResult ug mga pamaagi katapusanDoSomething nga midawat niini ug alang sa kabilin niining mga tuiga ang FromAsync nga pamaagi hingpit ra, apan sa paglabay sa panahon, kini gipulihan sa Event Based Asynchronous Pattern (EAP), nga nagtuo nga ang usa ka panghitabo mabanhaw kung ang asynchronous nga operasyon nahuman.

Ang TaskCompletionSource perpekto alang sa pagputos sa Mga Buluhaton ug mga kabilin nga API nga gitukod sa palibot sa modelo sa panghitabo. Ang esensya sa trabaho niini mao ang mosunod: ang usa ka butang niini nga klase adunay usa ka publiko nga propiedad sa tipo nga Task, ang estado nga mahimong kontrolado pinaagi sa SetResult, SetException, ug uban pa nga mga pamaagi sa TaskCompletionSource nga klase. Sa mga lugar diin gipadapat ang operator nga naghulat niini nga Buluhaton, kini ipatuman o mapakyas nga adunay eksepsiyon depende sa pamaagi nga gigamit sa TaskCompletionSource. Kung dili pa kini klaro, atong tan-awon kini nga pananglitan sa code, diin ang pipila ka karaan nga EAP API giputos sa usa ka Buluhaton gamit ang usa ka TaskCompletionSource: kung ang panghitabo sunog, ang Buluhaton ibalhin sa Nakumpleto nga estado, ug ang pamaagi nga gigamit sa naghulat nga operator. niini nga Buluhaton ipadayon ang pagpatuman niini nga nakadawat sa butang resulta.

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 Tips & Tricks

Ang pagputos sa daan nga mga API dili ang tanan nga mahimo gamit ang TaskCompletionSource. Ang paggamit niini nga klase nagbukas sa usa ka makapaikag nga posibilidad sa pagdesinyo sa lainlaing mga API sa Mga Buluhaton nga wala mag-okupar sa mga hilo. Ug ang sapa, ingon sa atong nahinumduman, usa ka mahal nga kapanguhaan ug ang ilang gidaghanon limitado (nag-una sa gidaghanon sa RAM). Kini nga limitasyon dali nga makab-ot pinaagi sa paghimo, pananglitan, usa ka puno nga aplikasyon sa web nga adunay komplikado nga lohika sa negosyo. Atong tagdon ang mga posibilidad nga akong gihisgutan sa dihang nagpatuman sa maong limbong sama sa Long-Polling.

Sa laktud, ang diwa sa lansis mao kini: kinahanglan nimo nga makadawat og impormasyon gikan sa API mahitungod sa pipila ka mga panghitabo nga nahitabo sa kilid niini, samtang ang API, sa usa ka rason, dili maka-report sa panghitabo, apan mahimo lamang ibalik ang estado. Usa ka pananglitan niini mao ang tanan nga mga API nga gitukod sa ibabaw sa HTTP sa wala pa ang mga panahon sa WebSocket o kung imposible alang sa pipila ka rason nga gamiton kini nga teknolohiya. Ang kliyente makapangutana sa HTTP server. Ang HTTP server dili mismo makasugod sa komunikasyon sa kliyente. Ang usa ka yano nga solusyon mao ang pag-poll sa server gamit ang usa ka timer, apan kini nagmugna og dugang nga load sa server ug usa ka dugang nga paglangan sa kasagaran nga TimerInterval / 2. Aron makalibut niini, usa ka limbong nga gitawag og Long Polling ang naimbento, nga naglakip sa paglangan sa tubag gikan sa ang server hangtod matapos ang Timeout o mahitabo ang usa ka panghitabo. Kung ang panghitabo nahitabo, nan kini giproseso, kung dili, ang hangyo ipadala pag-usab.

while(!eventOccures && !timeoutExceeded)  {

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

Apan ang ingon nga solusyon mapamatud-an nga makalilisang sa diha nga ang gidaghanon sa mga kliyente nga naghulat alang sa kalihokan nagdugang, tungod kay ... Ang matag ingon nga kliyente nag-okupar sa usa ka tibuuk nga thread nga naghulat alang sa usa ka panghitabo. Oo, ug nakakuha kami dugang nga 1ms nga paglangan kung ang panghitabo na-trigger, kasagaran dili kini hinungdanon, apan ngano nga himuon ang software nga labi ka daotan kaysa mahimo? Kung atong tangtangon ang Thread.Sleep(1), nan walay kapuslanan nga atong i-load ang usa ka processor nga core nga 100% idle, nga nagtuyok sa usa ka walay pulos nga siklo. Pinaagi sa paggamit sa TaskCompletionSource dali nimong mahimo ang kini nga code ug masulbad ang tanan nga mga problema nga nahibal-an sa ibabaw:

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);
    }
}

Kini nga code dili andam sa produksiyon, apan usa lamang ka demo. Aron magamit kini sa tinuod nga mga kaso, kinahanglan usab nimo, sa labing gamay, aron madumala ang sitwasyon kung ang usa ka mensahe moabut sa usa ka panahon nga wala’y nagpaabut niini: sa kini nga kaso, ang pamaagi sa AsseptMessageAsync kinahanglan nga ibalik ang usa ka nahuman na nga Buluhaton. Kung kini ang labing kasagaran nga kaso, mahimo nimong hunahunaon ang paggamit sa ValueTask.

Kung makadawat kami usa ka hangyo alang sa usa ka mensahe, maghimo kami ug magbutang usa ka TaskCompletionSource sa diksyonaryo, ug dayon maghulat kung unsa ang una nga mahitabo: ang gitakda nga agwat sa oras matapos o usa ka mensahe ang nadawat.

ValueTask: ngano ug giunsa

Ang async/wait operators, sama sa yield return operator, makamugna og state machine gikan sa pamaagi, ug kini ang pagmugna og bag-ong butang, nga halos kanunay dili importante, apan sa talagsaong mga kaso makamugna kini og problema. Kini nga kaso mahimo nga usa ka pamaagi nga gitawag sa kanunay, naghisgot kami bahin sa napulo ug gatusan ka libo nga mga tawag matag segundo. Kung ang ingon nga pamaagi gisulat sa paagi nga sa kadaghanan nga mga kaso nagbalik kini usa ka sangputanan nga wala’y paghulat sa tanan nga mga pamaagi, nan ang .NET naghatag usa ka himan aron ma-optimize kini - ang istruktura sa ValueTask. Aron maklaro kini, atong tan-awon ang usa ka pananglitan sa paggamit niini: adunay usa ka cache nga kanunay natong adtoan. Adunay pipila ka mga kantidad niini ug dayon ibalik lang namo kini; kung dili, nan moadto kami sa pipila ka hinay nga IO aron makuha kini. Gusto nako nga buhaton ang ulahi nga asynchronous, nga nagpasabut nga ang tibuuk nga pamaagi nahimo nga asynchronous. Busa, ang klaro nga paagi sa pagsulat sa pamaagi mao ang mosunod:

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

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

Tungod sa tinguha nga ma-optimize ang gamay, ug gamay nga kahadlok sa kung unsa ang mabuhat ni Roslyn sa pag-compile niini nga code, mahimo nimong isulat pag-usab kini nga pananglitan sama sa mosunod:

public Task<string> GetById(int id) {

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

Sa tinuud, ang kamalaumon nga solusyon sa kini nga kaso mao ang pag-optimize sa mainit nga dalan, nga mao, pagkuha usa ka kantidad gikan sa diksyonaryo nga wala’y bisan unsang wala kinahanglana nga mga alokasyon ug pagkarga sa GC, samtang sa mga talagsa nga mga kaso kung kinahanglan pa naton moadto sa IO alang sa datos , ang tanan magpabilin nga plus/minus sa karaang paagi:

public ValueTask<string> GetById(int id) {

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

Atong tan-awon pag-ayo kini nga piraso sa code: kung adunay usa ka bili sa cache, maghimo kita og usa ka istruktura, kung dili ang tinuod nga buluhaton maputos sa usa ka makahuluganon. Ang kodigo sa pagtawag wala magtagad kung asa nga dalan kini nga code gipatuman sa: ValueTask, gikan sa usa ka punto sa panglantaw sa C# syntax, molihok sama sa usa ka regular nga Buluhaton niini nga kaso.

Mga TaskScheduler: pagdumala sa mga estratehiya sa paglansad sa buluhaton

Ang sunod nga API nga gusto nakong ikonsiderar mao ang klase TaskScheduler ug mga derivatives niini. Nahisgotan na nako sa ibabaw nga ang TPL adunay katakus sa pagdumala sa mga estratehiya alang sa pag-apod-apod sa mga Buluhaton sa mga hilo. Ang ingon nga mga estratehiya gihubit sa mga kaliwat sa klase sa TaskScheduler. Halos bisan unsang estratehiya nga mahimo nimo nga kinahanglan makit-an sa librarya. ParallelExtensionsExtras, naugmad sa Microsoft, apan dili bahin sa .NET, apan gihatag isip usa ka pakete sa Nuget. Atong tan-awon sa daklit ang pipila niini:

  • CurrentThreadTaskScheduler — nagpatuman sa mga Buluhaton sa kasamtangan nga hilo
  • Limitado ngaConcurrencyLevelTaskScheduler - limitahan ang gidaghanon sa mga Buluhaton nga gipatuman dungan sa parameter N, nga gidawat sa constructor
  • Gi-order ngaTaskScheduler — gihubit ingong LimitedConcurrencyLevelTaskScheduler(1), mao nga ang mga buluhaton sunodsunod nga ipatuman.
  • WorkStealingTaskScheduler - nagpatuman pagpangawat sa trabaho pamaagi sa pag-apod-apod sa buluhaton. Sa tinuud kini usa ka lahi nga ThreadPool. Nasulbad ang problema nga sa .NET ThreadPool usa ka static nga klase, usa alang sa tanan nga mga aplikasyon, nga nagpasabut nga ang sobra nga pagkarga o dili husto nga paggamit sa usa ka bahin sa programa mahimong mosangput sa mga epekto sa lain. Dugang pa, lisod kaayong sabton ang hinungdan sa maong mga depekto. Nga. Mahimong adunay panginahanglan sa paggamit sa bulag nga WorkStealingTaskSchedulers sa mga bahin sa programa diin ang paggamit sa ThreadPool mahimong agresibo ug dili matag-an.
  • QeuedTaskScheduler - nagtugot kanimo sa paghimo sa mga buluhaton sumala sa prayoridad nga mga lagda sa pila
  • ThreadPerTaskScheduler — nagmugna ug lahi nga hilo alang sa matag Buluhaton nga gipatuman niini. Mahimong mapuslanon alang sa mga buluhaton nga dili matag-an nga dugay mahuman.

Adunay maayo nga detalyado nga artikulo mahitungod sa TaskSchedulers sa microsoft blog.

Alang sa sayon ​​nga pag-debug sa tanang butang nga may kalabotan sa Mga Buluhaton, ang Visual Studio adunay bintana sa Mga Buluhaton. Niini nga bintana imong makita ang kasamtangan nga kahimtang sa buluhaton ug molukso ngadto sa kasamtangang nagpatuman nga linya sa code.

.NET: Mga himan alang sa pagtrabaho sa multithreading ug asynchrony. Bahin 1

PLinq ug ang Parallel nga klase

Dugang sa Mga Buluhaton ug ang tanan nga gisulti bahin kanila, adunay duha pa ka makapaikag nga mga himan sa .NET: PLinq (Linq2Parallel) ug ang Parallel nga klase. Ang una nagsaad nga managsama nga pagpatuman sa tanan nga operasyon sa Linq sa daghang mga hilo. Ang gidaghanon sa mga hilo mahimong ma-configure gamit ang WithDegreeOfParallelism extension method. Ikasubo, kasagaran ang PLinq sa default mode niini walay igong impormasyon mahitungod sa internals sa imong data source aron makahatag og dakong katulin, sa laing bahin, ang gasto sa pagsulay ubos kaayo: kinahanglan nimo nga tawagan ang AsParallel nga pamaagi sa wala pa ang kadena sa mga pamaagi sa Linq ug nagpadagan sa mga pagsulay sa pasundayag. Dugang pa, posible nga ipasa ang dugang nga kasayuran sa PLinq bahin sa kinaiyahan sa imong gigikanan sa datos gamit ang mekanismo sa Partitions. Makabasa ka ug dugang dinhi и dinhi.

Ang Parallel static nga klase naghatag ug mga pamaagi sa pag-uli sa usa ka Foreach nga koleksyon nga managsama, pag-execute sa For loop, ug pag-execute sa daghang mga delegado sa parallel Invoke. Ang pagpatuman sa kasamtangan nga hilo ihunong hangtod mahuman ang mga kalkulasyon. Ang gidaghanon sa mga hilo mahimong ma-configure pinaagi sa pagpasa sa ParallelOptions isip katapusang argumento. Mahimo usab nimo ipiho ang TaskScheduler ug CancellationToken gamit ang mga kapilian.

kaplag

Sa dihang nagsugod ako sa pagsulat niini nga artikulo base sa mga materyales sa akong report ug sa impormasyon nga akong nakolekta atol sa akong trabaho human niini, wala ko magdahom nga daghan kaayo niini. Karon, kung ang editor sa teksto diin ako nag-type niini nga artikulo mapasipalahon nga nagsulti kanako nga ang panid 15 nawala, akong i-summarize ang interim nga mga resulta. Ang ubang mga limbong, mga API, mga gamit sa biswal ug mga lit-ag matabonan sa sunod nga artikulo.

Mga konklusyon:

  • Kinahanglan nimo mahibal-an ang mga himan alang sa pagtrabaho sa mga hilo, asynchrony ug parallelism aron magamit ang mga kapanguhaan sa mga modernong PC.
  • .NET adunay daghang lain-laing mga himan alang niini nga mga katuyoan
  • Dili tanan niini nagpakita sa usa ka higayon, mao nga kanunay nimo makit-an ang mga kabilin, bisan pa, adunay mga paagi aron mabag-o ang mga daan nga API nga wala’y daghang paningkamot.
  • Ang pagtrabaho sa mga thread sa .NET girepresentahan sa mga klase sa Thread ug ThreadPool
  • Ang Thread.Abort, Thread.Interrupt, ug Win32 API TerminateThread nga mga pamaagi delikado ug dili girekomenda nga gamiton. Hinuon, mas maayo nga gamiton ang mekanismo sa CancellationToken
  • Ang agos usa ka bililhon nga kapanguhaan ug ang suplay niini limitado. Ang mga sitwasyon diin ang mga thread busy sa paghulat sa mga panghitabo kinahanglan nga likayan. Alang niini sayon ​​​​nga gamiton ang klase sa TaskCompletionSource
  • Ang labing gamhanan ug abante nga .NET nga mga himan alang sa pagtrabaho uban sa paralelismo ug asynchrony mao ang mga Buluhaton.
  • Ang c# async/wait operators nagpatuman sa konsepto sa non-blocking wait
  • Mahimo nimong kontrolon ang pag-apod-apod sa mga Buluhaton sa mga hilo gamit ang mga klase nga nakuha sa TaskScheduler
  • Ang ValueTask structure mahimong mapuslanon sa pag-optimize sa mga hot-path ug memory-traffic
  • Ang Visual Studio's Tasks and Threads windows naghatag og daghang impormasyon nga mapuslanon alang sa pag-debug sa multi-threaded o asynchronous nga code
  • Ang PLinq usa ka cool nga himan, apan kini mahimo nga wala’y igong kasayuran bahin sa imong gigikanan sa datos, apan mahimo kini nga ayohon gamit ang mekanismo sa pagbahin.
  • Ipadayon…

Source: www.habr.com

Idugang sa usa ka comment