.NET: Zouti pou travay ak multithreading ak asynchrony. Pati 1

Mwen pibliye atik orijinal la sou Habr, tradiksyon an ki afiche nan antrepriz la pòs blog la.

Bezwen pou fè yon bagay asynchrone, san yo pa tann pou rezilta a isit la ak kounye a, oswa divize gwo travay nan mitan plizyè inite fè li, te egziste anvan avènement nan òdinatè. Avèk avenman yo, bezwen sa a te vin trè byen mèb. Koulye a, nan 2019, mwen tape atik sa a sou yon laptop ak yon processeur Intel Core 8-nwayo, sou ki plis pase yon santèn pwosesis ap kouri nan paralèl, ak menm plis fil. Toupre, gen yon telefòn yon ti kras ranyon, te achte yon koup de ane de sa, li gen yon processeur 8-debaz sou tablo. Resous tematik yo plen ak atik ak videyo kote otè yo admire smartphones bato ane sa a ki prezante processeur 16-nwayo. MS Azure bay yon machin vityèl ak yon processeur debaz 20 ak 128 TB RAM pou mwens pase $ 2 / èdtan. Malerezman, li enposib ekstrè maksimòm ak ekipe pouvwa sa a san yo pa kapab jere entèraksyon an nan fil.

Tèminoloji

Pwosesis - OS objè, espas adrès izole, gen fil.
Fil - yon objè OS, inite ki pi piti nan ekzekisyon, yon pati nan yon pwosesis, fil pataje memwa ak lòt resous nan mitan yo nan yon pwosesis.
Multitech - OS pwopriyete, kapasite nan kouri plizyè pwosesis ansanm
Milti-nwayo - yon pwopriyete nan processeur a, kapasite nan sèvi ak plizyè nwayo pou pwosesis done
Multiprocessing - yon pwopriyete nan yon òdinatè, kapasite nan ansanm travay ak processeurs plizyè fizikman
Multithreading — yon pwopriyete nan yon pwosesis, kapasite nan distribye pwosesis done nan mitan plizyè fil.
Paralelis - fè plizyè aksyon fizikman an menm tan pou chak inite tan
Asynchrony — ekzekisyon yon operasyon san yo pa tann pou fini pwosesis sa a; rezilta egzekisyon an ka trete pita.

Metafò

Se pa tout definisyon ki bon epi kèk bezwen plis eksplikasyon, kidonk mwen pral ajoute yon metafò sou kwit manje maten nan tèminoloji fòmèlman prezante a. Kwit manje maten nan metafò sa a se yon pwosesis.

Pandan m ap prepare manje maten nan maten mwen (CPU) Mwen vin nan kwizin nan (Odinatè). Mwen gen 2 men (Am). Gen yon kantite aparèy nan kwizin nan (IO): fou, kettle, griye, frijidè. Mwen limen gaz la, mete yon chodyè sou li epi vide lwil nan li san tann li chofe (asynchrone, ki pa bloke-IO-tann), mwen pran ze yo soti nan frijidè a epi kraze yo nan yon plak, Lè sa a, bat yo ak yon sèl men (Fil #1), ak dezyèm (Fil #2) kenbe plak la (Shared Resource). Koulye a, mwen ta renmen limen kettle a, men mwen pa gen ase men (Fil grangou) Pandan tan sa a, chodyè a chofe (Trasesan rezilta a) nan kote mwen vide sa mwen te vide. Mwen rive jwenn bouyi a epi limen li epi mwen gade dlo a bouyi ladan l.Bloke-IO-Tann), byenke pandan tan sa a li te kapab lave plak la kote li fwe omelet la.

Mwen te kwit yon omlèt avèk 2 men sèlman, epi mwen pa gen plis, men an menm tan, nan moman fwete a, 3 operasyon te fèt nan yon fwa: fwete a, kenbe plak la, chofe chodyè a. CPU a se pati ki pi rapid nan òdinatè a, IO se sa ki pi souvan tout bagay ralanti, kidonk souvan yon solisyon efikas se okipe CPU a ak yon bagay pandan y ap resevwa done ki soti nan IO.

Kontinye metafò a:

  • Si nan pwosesis la nan prepare yon omlèt, mwen ta tou eseye chanje rad, sa a ta dwe yon egzanp nan multitech. Yon nuans enpòtan: òdinatè yo pi bon nan sa a pase moun.
  • Yon kwizin ak plizyè chèf, pou egzanp nan yon restoran - yon òdinatè milti-nwayo.
  • Anpil restoran nan yon tribinal manje nan yon sant komèsyal - sant done

.NET Zouti

.NET bon nan travay ak fil, menm jan ak anpil lòt bagay. Avèk chak nouvo vèsyon, li prezante pi plis ak plis nouvo zouti pou travay avèk yo, nouvo kouch abstraksyon sou fil OS. Lè w ap travay ak konstriksyon abstraksyon, devlopè kad yo itilize yon apwòch ki kite opòtinite, lè w ap itilize yon abstraksyon wo nivo, pou desann youn oswa plizyè nivo anba a. Pi souvan sa a pa nesesè, an reyalite li louvri pòt la tire tèt ou nan pye a ak yon fizi, men pafwa, nan ka ra, li ka sèlman fason yo rezoud yon pwoblèm ki pa rezoud nan nivo aktyèl la nan abstrè. .

Pa zouti, mwen vle di tou de koòdone pwogramasyon aplikasyon (APIs) ki ofri pa kad la ak pakè twazyèm pati, osi byen ke solisyon lojisyèl antye ki senplifye rechèch la pou nenpòt pwoblèm ki gen rapò ak kòd milti-threaded.

Kòmanse yon fil

Klas Thread la se klas ki pi fondamantal nan .NET pou travay ak fil. Konstriksyon an aksepte youn nan de delege:

  • ThreadStart — Pa gen paramèt
  • ParametrizedThreadStart - ak yon sèl paramèt nan kalite objè.

Delege a pral egzekite nan fil ki fèk kreye apre yo fin rele metòd Start la.Si yo te pase yon delege nan kalite ParametrizedThreadStart nan konstrukteur a, Lè sa a, yon objè dwe pase nan metòd la Start. Mekanis sa a nesesè pou transfere nenpòt enfòmasyon lokal nan kouran an. Li se vo anyen ke kreye yon fil se yon operasyon chè, ak fil nan tèt li se yon objè lou, omwen paske li asiyen 1MB nan memwa sou chemine a epi li mande pou entèraksyon ak OS API a.

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

Klas ThreadPool reprezante konsèp yon pisin. Nan .NET, pisin nan fil se yon moso nan jeni, ak devlopè yo nan Microsoft te fè anpil efò nan asire w ke li travay optimal nan yon gran varyete senaryo.

Konsèp jeneral:

Soti nan moman aplikasyon an kòmanse, li kreye plizyè fil an rezèv nan background nan epi li bay kapasite nan pran yo pou itilize. Si yo itilize fil souvan epi an gwo kantite, pisin lan elaji pou satisfè bezwen moun k ap rele a. Lè pa gen okenn fil gratis nan pisin lan nan bon moman an, li pral swa tann pou youn nan fil yo retounen, oswa kreye yon nouvo. Li swiv ke pisin nan fil se gwo pou kèk aksyon kout tèm ak mal adapte pou operasyon ki kouri kòm sèvis pandan tout operasyon an nan aplikasyon an.

Pou itilize yon fil ki soti nan pisin lan, gen yon metòd QueueUserWorkItem ki aksepte yon delege nan kalite WaitCallback, ki gen menm siyati ak ParametrizedThreadStart, ak paramèt pase a li fè menm fonksyon an.

ThreadPool.QueueUserWorkItem(...);

Metòd pisin fil ki pi piti yo konnen RegisterWaitForSingleObject yo itilize pou òganize operasyon IO ki pa bloke. Yo pral rele delege ki te pase nan metòd sa a lè WaitHandle te pase nan metòd la se "Released".

ThreadPool.RegisterWaitForSingleObject(...)

.NET gen yon revèy fil epi li diferan de revèy WinForms/WPF nan ke moun ki okipe li yo pral rele sou yon fil pran nan pisin lan.

System.Threading.Timer

Genyen tou yon fason olye ekzotik voye yon delege pou ekzekisyon nan yon fil soti nan pisin lan - metòd BeginInvoke.

DelegateInstance.BeginInvoke

Mwen ta renmen rete yon ti tan sou fonksyon an ke anpil nan metòd ki anwo yo ka rele - CreateThread soti nan Kernel32.dll Win32 API. Gen yon fason, gras a mekanis nan metòd ekstèn, yo rele fonksyon sa a. Mwen te wè yon apèl konsa sèlman yon fwa nan yon egzanp terib nan kòd eritaj, ak motivasyon an nan otè a ki te fè egzakteman sa a toujou rete yon mistè pou mwen.

Kernel32.dll CreateThread

Wè ak debogaj Threads

Fil yo kreye pa ou, tout eleman twazyèm pati, ak pisin lan .NET ka wè nan fenèt la Fil nan Visual Studio. Fenèt sa a pral sèlman montre enfòmasyon sou fil lè aplikasyon an anba debug ak nan mòd Break. Isit la ou ka fasilman wè non pil ak priyorite chak fil, epi chanje debogaj nan yon fil espesifik. Sèvi ak pwopriyete priyorite nan klas Thread la, ou ka mete priyorite yon fil, ki OC ak CLR pral wè kòm yon rekòmandasyon lè divize tan processeur ant fil.

.NET: Zouti pou travay ak multithreading ak asynchrony. Pati 1

Objektif Bibliyotèk Paralèl

Task Parallel Library (TPL) te prezante nan .NET 4.0. Koulye a, li se estanda a ak zouti prensipal la pou travay ak asynchrony. Nenpòt kòd ki itilize yon apwòch ki pi gran yo konsidere kòm eritaj. Inite debaz TPL se klas Task ki soti nan espas non System.Threading.Tasks. Yon travay se yon abstraksyon sou yon fil. Avèk nouvo vèsyon lang C# a, nou jwenn yon fason elegant pou travay avèk Tasks - async/wait operators. Konsèp sa yo te fè li posib yo ekri kòd asynchrone kòm si li te senp ak synchrone, sa te fè li posib menm pou moun ki gen ti konpreyansyon nan fonksyonman entèn fil yo ekri aplikasyon ki sèvi ak yo, aplikasyon ki pa friz lè yo fè operasyon long. Sèvi ak async/wait se yon sijè pou youn oswa menm plizyè atik, men mwen pral eseye jwenn esansyèl la nan kèk fraz:

  • async se yon modifikasyon nan yon metòd retounen Task oswa anile
  • ak tann se yon operatè ki pa bloke Travay ap tann.

Yon lòt fwa ankò: operatè a tann, nan ka jeneral la (gen eksepsyon), pral lage fil aktyèl la nan ekzekisyon pi lwen, epi lè Travay la fini ekzekisyon li yo, ak fil la (an reyalite, li ta pi kòrèk pou di kontèks la. , men plis sou sa pita) ap kontinye egzekite metòd la pi lwen. Anndan .NET, se mekanis sa a aplike nan menm fason an kòm retounen rendement, lè metòd la ekri tounen yon klas antye, ki se yon machin leta epi yo ka egzekite nan moso separe depann sou eta sa yo. Nenpòt moun ki enterese ka ekri nenpòt kòd senp lè l sèvi avèk asynс/wait, konpile ak wè asanble a lè l sèvi avèk JetBrains dotPeek ak Kòd Jenere Konpilatè pèmèt.

Ann gade nan opsyon pou lanse ak itilize Task. Nan egzanp kòd ki anba a, nou kreye yon nouvo travay ki pa fè anyen itil (Thread.Sleep (10000)), men nan lavi reyèl sa a ta dwe kèk travay konplèks CPU-entansif.

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
}

Yon Travay kreye ak yon kantite opsyon:

  • LongRunning se yon allusion ke travay la pa pral fini byen vit, ki vle di li ka vo konsidere pa pran yon fil nan pisin lan, men kreye yon sèl separe pou Travay sa a pou yo pa fè lòt moun mal.
  • AttachedToParent - Travay yo ka ranje nan yon yerachi. Si yo te itilize opsyon sa a, Lè sa a, Travay la ka nan yon eta kote li menm li te konplete epi li ap tann pou ekzekisyon an nan pitit li yo.
  • PreferFairness - vle di ke li ta pi bon pou egzekite Travay yo voye pou ekzekisyon pi bonè anvan sa yo voye pita. Men, sa a se jis yon rekòmandasyon ak rezilta yo pa garanti.

Dezyèm paramèt ki pase nan metòd la se CancellationToken. Pou kòrèkteman jere anile yon operasyon apre li te kòmanse, kòd ke yo te egzekite a dwe ranpli ak chèk pou eta a CancellationToken. Si pa gen okenn chèk, Lè sa a, metòd la Anile yo rele sou objè a CancellationTokenSource yo pral kapab sispann ekzekisyon an nan Travay la sèlman anvan li kòmanse.

Dènye paramèt la se yon objè pwogramè nan kalite TaskScheduler. Klas sa a ak desandan li yo fèt pou kontwole estrateji pou distribye Travay yo atravè fil; pa default, Travay la pral egzekite sou yon fil owaza soti nan pisin lan.

Operatè tann lan aplike nan travay la kreye, ki vle di kòd la ekri apre li, si gen youn, yo pral egzekite nan menm kontèks la (souvan sa vle di sou menm fil la) kòm kòd la anvan tann.

Metòd la make kòm anile async, ki vle di li ka sèvi ak operatè await la, men kòd apèl la pa pral kapab tann pou ekzekisyon an. Si tankou yon karakteristik nesesè, Lè sa a, metòd la dwe retounen Objektif Travay la. Metòd ki make async anile yo byen komen: kòm yon règ, sa yo se moun kap okipe evènman oswa lòt metòd ki travay sou dife a epi bliye prensip. Si ou bezwen pa sèlman bay opòtinite pou yo rete tann jouk nan fen ekzekisyon an, men tou, retounen rezilta a, Lè sa a, ou bezwen sèvi ak Objektif Travay la.

Sou Objektif Travay la ke metòd StartNew la te retounen, osi byen ke sou nenpòt lòt, ou ka rele metòd la ConfigureAwait ak fo paramèt la, Lè sa a, ekzekisyon apre tann ap kontinye pa sou kontèks la kaptire, men sou yon sèl abitrè. Sa a ta dwe toujou fè lè kontèks ekzekisyon an pa enpòtan pou kòd la apre tann. Sa a se tou yon rekòmandasyon nan MS lè w ap ekri kòd ki pral delivre pake nan yon bibliyotèk.

Ann rete yon ti kras plis sou ki jan ou ka tann pou fini an nan yon Travay. Anba a se yon egzanp kòd, ak kòmantè sou lè atant yo fè kondisyonèl byen ak lè li se fè kondisyonèl mal.

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
}

Nan premye egzanp lan, nou tann pou Travay la fini san yo pa bloke fil apèl la; nou pral retounen nan trete rezilta a sèlman lè li deja la; jouk lè sa a, fil apèl la kite nan pwòp aparèy li yo.

Nan dezyèm opsyon an, nou bloke fil apèl la jiskaske rezilta metòd la kalkile. Sa a se move pa sèlman paske nou te okipe yon fil, tankou yon resous valab nan pwogram nan, ak ochomaj senp, men tou paske si kòd la nan metòd la ke nou rele gen tann, ak kontèks la senkronizasyon mande pou retounen nan fil la rele apre. tann, Lè sa a, nou pral jwenn yon enpas : Fil apèl la ap tann pou rezilta metòd asynchrone a dwe kalkile, metòd asynchrone a eseye pou gremesi kontinye ekzekisyon li nan fil apèl la.

Yon lòt dezavantaj nan apwòch sa a se manyen erè konplike. Reyalite a se ke erè nan kòd asynchrone lè w ap itilize async/wait yo trè fasil okipe - yo konpòte menm jan ak si kòd la te synchrone. Pandan ke si nou aplike synchrone tann exorcism pou yon travay, eksepsyon orijinal la tounen yon AggregateException, i.e. Pou okipe eksepsyon an, w ap oblije egzamine kalite InnerException la epi ekri yon chèn si ou menm andedan yon blòk trape oswa itilize trape an lè konstwi, olye pou yo chèn blòk trape ki pi abitye nan mond C#.

Twazyèm ak dènye egzanp yo tou make move pou menm rezon an epi yo genyen tout menm pwoblèm yo.

Metòd WhenAny ak WhenAll yo trè pratik pou tann yon gwoup Travay; yo vlope yon gwoup Travay nan yon sèl, ki pral tire swa lè yon Travay nan gwoup la premye deklanche, oswa lè yo tout fin egzekisyon yo.

Sispann fil

Pou plizyè rezon, li ka nesesè pou sispann koule a apre li te kòmanse. Gen yon kantite fason pou fè sa. Klas Thread la gen de metòd ki apwopriye: Avòt и Entèwonp. Premye a trè pa rekòmande pou itilize, paske apre yo fin rele li nan nenpòt moman o aza, pandan pwosesis la nan nenpòt enstriksyon, yo pral jete yon eksepsyon. ThreadAbortedException. Ou pa atann yon eksepsyon sa yo dwe jete lè enkreman nenpòt varyab nonb antye relatif, dwa? Ak lè w ap itilize metòd sa a, sa a se yon sitiyasyon trè reyèl. Si ou bezwen anpeche CLR a jenere yon eksepsyon konsa nan yon sèten seksyon nan kòd, ou ka vlope li nan apèl. Thread.BeginCriticalRegion, Thread.EndCriticalRegion. Nenpòt kòd ki ekri nan yon blòk finalman vlope nan apèl sa yo. Pou rezon sa a, nan pwofondè yo nan kòd la kad ou ka jwenn blòk ak yon eseye vid, men se pa yon vid finalman. Microsoft dekouraje metòd sa a anpil ke yo pa t 'gen ladan li nan nwayo .net.

Metòd entèwonp lan travay pi previzib. Li ka entèwonp fil la ak yon eksepsyon ThreadInterruptedException sèlman pandan moman sa yo lè fil la nan yon eta ap tann. Li antre nan eta sa a pandan w ap pandye pandan w ap tann WaitHandle, lock, oswa apre w fin rele Thread.Sleep.

Tou de opsyon ki dekri pi wo a se move akòz enprevizib yo. Solisyon an se sèvi ak yon estrikti CancellationToken ak klas CancellationTokenSource. Pwen an se sa a: yon egzanp nan klas la CancellationTokenSource kreye epi sèlman moun ki posede li ka sispann operasyon an lè li rele metòd la. Anile. Se sèlman CancellationToken la pase nan operasyon an tèt li. Pwopriyetè CancellationToken pa ka anile operasyon an tèt yo, men yo ka tcheke sèlman si yo te anile operasyon an. Gen yon pwopriyete Boolean pou sa a IsCancellationRequested ak metòd ThrowIfCancelRequested. Lèt la pral voye yon eksepsyon TaskCancelledException si yo te rele metòd Anile sou egzanp CancellationToken ke yo te parroted. Ak sa a se metòd la mwen rekòmande pou itilize. Sa a se yon amelyorasyon sou opsyon anvan yo pa pran kontwòl konplè sou ki pwen yon operasyon eksepsyon ka avòte.

Opsyon ki pi brital pou sispann yon fil se rele fonksyon Win32 API TerminateThread. Konpòtman CLR a apre yo fin rele fonksyon sa a ka enprevizib. Sou MSDN sa ki annapre yo ekri sou fonksyon sa a: "TerminateThread se yon fonksyon danjere ki ta dwe itilize sèlman nan ka ki pi ekstrèm yo. "

Konvèti API eritaj nan Task Baze lè l sèvi avèk metòd FromAsync

Si ou gen ase chans pou w travay sou yon pwojè ki te kòmanse apre yo te prezante Tasks epi yo te sispann lakòz laterè trankil pou pifò devlopè, Lè sa a, ou pa pral oblije fè fas ak yon anpil nan ansyen APIs, tou de twazyèm pati yo ak sa yo ekip ou a. te tòtire nan tan lontan an. Erezman, ekip .NET Framework te pran swen nou, byenke petèt objektif la se te pran swen tèt nou. Se pou sa, .NET gen yon kantite zouti pou san doulè konvèti kòd ekri nan ansyen apwòch pwogram asynchrone nan nouvo a. Youn nan yo se metòd FromAsync TaskFactory. Nan egzanp kòd ki anba a, mwen vlope ansyen metòd async klas WebRequest la nan yon Task lè l sèvi avèk metòd sa a.

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

Sa a se jis yon egzanp epi ou pa fasil pou w fè sa ak kalite entegre, men nenpòt ki ansyen pwojè se tou senpleman gronde ak metòd BeginDoSomething ki retounen IAsyncResult ak EndDoSomething metòd ki resevwa li.

Konvèti API eritaj nan Task Based lè l sèvi avèk klas TaskCompletionSource

Yon lòt zouti enpòtan pou konsidere se klas la TaskCompletionSource. An tèm de fonksyon, objektif ak prensip operasyon an, li ka yon ti jan okoumansman de metòd RegisterWaitForSingleObject nan klas ThreadPool, ke mwen te ekri sou pi wo a. Sèvi ak klas sa a, ou ka fasilman ak fasilman vlope ansyen API asynchrone nan Tasks.

Ou pral di ke mwen te deja pale sou metòd FromAsync nan klas la TaskFactory gen entansyon pou rezon sa yo. Isit la nou pral oblije sonje tout istwa devlopman modèl asynchrone nan .net ke Microsoft te ofri pandan 15 ane ki sot pase yo: anvan Task-Based Asynchrone Pattern (TAP), te gen Asynchrone Programming Pattern (APP), ki te genyen. te sou metòd KòmanseFè yon bagay ap retounen IAsyncResult ak metòd FenFè yon bagay ki aksepte li epi pou eritaj ane sa yo metòd FromAsync jis pafè, men apre yon sèten tan, li te ranplase pa Evènman ki baze sou Asynchrone Pattern (AK AP), ki te sipoze ke yon evènman ta dwe leve soti vivan lè operasyon an asynchrone fini.

TaskCompletionSource pafè pou vlope Tasks ak eritaj API ki te bati nan modèl evènman an. Sans nan travay li se jan sa a: yon objè nan klas sa a gen yon pwopriyete piblik nan kalite Task, eta a nan ki ka kontwole atravè metòd la SetResult, SetException, elatriye nan klas la TaskCompletionSource. Nan kote yo te aplike operatè await la nan Travay sa a, li pral egzekite oswa echwe ak yon eksepsyon depann sou metòd la aplike nan TaskCompletionSource la. Si li toujou pa klè, ann gade egzanp kòd sa a, kote kèk ansyen API EAP anvlope nan yon Task lè l sèvi avèk yon TaskCompletionSource: lè evènman an dife, Task la pral transfere nan eta a Konplete, ak metòd ki te aplike operatè a ap tann. Travay sa a pral rekòmanse egzekisyon li lè li te resevwa objè a lakòz.

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 Konsèy ak Trik

Anbalaj ansyen API se pa tout sa ki ka fè lè l sèvi avèk TaskCompletionSource. Sèvi ak klas sa a ouvè yon posibilite enteresan nan desine divès API sou Travay ki pa okipe fil. Ak kouran an, jan nou sonje, se yon resous chè ak kantite yo limite (sitou pa kantite RAM). Limit sa a ka fasil reyalize pa devlope, pou egzanp, yon aplikasyon entènèt chaje ak lojik biznis konplèks. Ann konsidere posiblite yo ke m ap pale sou yo lè aplike tankou yon Trick tankou Long-Polling.

Nan ti bout tan, sans nan jwe fent la se sa a: ou bezwen resevwa enfòmasyon ki soti nan API a sou kèk evènman ki fèt sou bò li yo, pandan y ap API a, pou kèk rezon, pa ka rapòte evènman an, men li ka sèlman retounen eta a. Yon egzanp sa yo se tout API ki te konstwi sou tèt HTTP anvan lè WebSocket oswa lè li te enposib pou kèk rezon pou itilize teknoloji sa a. Kliyan an ka mande sèvè HTTP a. Sèvè HTTP a pa ka li menm kòmanse kominikasyon ak kliyan an. Yon solisyon senp se sondaj sèvè a lè l sèvi avèk yon revèy, men sa a kreye yon chaj adisyonèl sou sèvè a ak yon reta adisyonèl an mwayèn TimerInterval / 2. Pou jwenn alantou sa a, yo te envante yon Trick ki rele Long Polling, ki enplike retade repons lan soti nan. sèvè a jiskaske Timeout la ekspire oswa yon evènman pral rive. Si evènman an te fèt, Lè sa a, li trete, si se pa, Lè sa a, demann lan voye ankò.

while(!eventOccures && !timeoutExceeded)  {

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

Men, tankou yon solisyon pral pwouve ke yo dwe terib le pli vit ke kantite kliyan ap tann pou evènman an ogmante, paske ... Chak kliyan sa yo okipe yon fil antye ap tann pou yon evènman. Wi, epi nou jwenn yon lòt reta 1ms lè evènman an deklanche, pi souvan sa a pa enpòtan, men poukisa fè lojisyèl an vin pi mal pase sa li kapab? Si nou retire Thread.Sleep (1), Lè sa a, pou gremesi nou pral chaje yon nwayo processeur 100% san fè anyen konsa, wotasyon nan yon sik initil. Sèvi ak TaskCompletionSource ou ka fasilman refè kòd sa a epi rezoud tout pwoblèm yo idantifye pi wo a:

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

Kòd sa a pa pare pou pwodiksyon, men se jis yon Demo. Pou itilize li nan ka reyèl, ou bezwen tou, nan yon minimòm, jere sitiyasyon an lè yon mesaj rive nan yon moman kote pa gen moun ki ap tann li: nan ka sa a, metòd la AsseptMessageAsync ta dwe retounen yon Travay ki deja ranpli. Si sa a se ka ki pi komen, Lè sa a, ou ka panse sou itilize ValueTask.

Lè nou resevwa yon demann pou yon mesaj, nou kreye epi mete yon TaskCompletionSource nan diksyonè a, epi tann sa k ap pase an premye: entèval tan espesifye a ekspire oswa yo resevwa yon mesaj.

ValueTask: poukisa ak ki jan

Operatè yo async / tann, tankou operatè a retounen sede, jenere yon machin eta soti nan metòd la, e sa a se kreyasyon an nan yon nouvo objè, ki se prèske toujou pa enpòtan, men nan ka ki ra li ka kreye yon pwoblèm. Ka sa a ka yon metòd ki rele vrèman souvan, nou ap pale de dizèn ak dè santèn de milye de apèl pa segonn. Si yon metòd sa yo ekri nan yon fason ke nan pifò ka li retounen yon rezilta contournement tout metòd tann, Lè sa a, .NET bay yon zouti pou optimize sa a - estrikti a ValueTask. Pou fè li klè, ann gade nan yon egzanp itilizasyon li yo: gen yon kachèt ke nou ale nan trè souvan. Gen kèk valè ladan l epi nou tou senpleman retounen yo; si se pa sa, Lè sa a, nou ale nan kèk IO dousman jwenn yo. Mwen vle fè lèt la asynchrone, ki vle di tout metòd la vire soti yo dwe asynchrone. Kidonk, fason evidan yo ekri metòd la se jan sa a:

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

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

Akòz dezi a optimize yon ti kras, ak yon ti kras pè nan sa Roslyn pral jenere lè w ap konpile kòd sa a, ou ka reekri egzanp sa a jan sa a:

public Task<string> GetById(int id) {

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

Vreman vre, solisyon an pi bon nan ka sa a ta dwe optimize chemen an cho, sètadi, jwenn yon valè nan diksyonè a san okenn alokasyon nesesè ak chaj sou GC a, pandan y ap nan ka sa yo ki ra lè nou toujou bezwen ale nan IO pou done. , tout bagay ap rete yon plis / mwens ansyen fason an:

public ValueTask<string> GetById(int id) {

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

Ann pran yon gade pi pre nan moso kòd sa a: si gen yon valè nan kachèt la, nou kreye yon estrikti, otreman travay reyèl la pral vlope nan yon sèl ki gen sans. Kòd apèl la pa pran swen ki chemen kòd sa a te egzekite nan: ValueTask, nan yon pwen de vi sentaks C#, pral konpòte menm jan ak yon Task regilye nan ka sa a.

TaskSchedulers: jere estrateji lansman travay

Pwochen API ke mwen ta renmen konsidere se klas la TaskScheduler ak dérivés li yo. Mwen deja mansyone pi wo a ke TPL gen kapasite nan jere estrateji pou distribye Travay atravè fil. Estrateji sa yo defini nan desandan klas TaskScheduler la. Prèske nenpòt estrateji ou ta ka bezwen ka jwenn nan bibliyotèk la. ParallelExtensionsExtras, devlope pa Microsoft, men se pa yon pati nan .NET, men apwovizyone kòm yon pake Nuget. Ann gade yon ti tan nan kèk nan yo:

  • CurrentThreadTaskScheduler — egzekite Travay sou fil aktyèl la
  • LimitedConcurrencyLevelTaskScheduler - limite kantite Travay egzekite an menm tan pa paramèt N, ki aksepte nan konstrukteur la
  • OrderedTaskScheduler — defini kòm LimitedConcurrencyLevelTaskScheduler(1), kidonk travay yo pral egzekite sekans.
  • WorkStealingTaskScheduler - aplike travay-vòlè apwòch distribisyon travay. Esansyèlman li se yon ThreadPool separe. Rezoud pwoblèm nan ki nan .NET ThreadPool se yon klas estatik, youn pou tout aplikasyon, ki vle di ke twòp chaj li yo oswa itilizasyon kòrèk nan yon pati nan pwogram nan ka mennen nan efè segondè nan yon lòt. Anplis, li trè difisil pou konprann kòz defo sa yo. Sa. Ka gen yon bezwen sèvi ak WorkStealingTaskSchedulers separe nan pati nan pwogram nan kote itilizasyon ThreadPool ka agresif ak enprevizib.
  • QueuedTaskScheduler — pèmèt ou fè travay selon règ priyorite keu
  • ThreadPerTaskScheduler — kreye yon fil separe pou chak Travay ki egzekite sou li. Kapab itil pou travay ki pran yon tan enprevizib pou konplete.

Gen yon bon detay atik sou TaskSchedulers sou blog Microsoft la.

Pou debogaj pratik nan tout bagay ki gen rapò ak Travay, Visual Studio gen yon fenèt Travay. Nan fenèt sa a ou ka wè eta aktyèl la nan travay la epi ale nan liy lan kounye a egzekite nan kòd.

.NET: Zouti pou travay ak multithreading ak asynchrony. Pati 1

PLinq ak klas Paralèl la

Anplis Travay ak tout sa ki di sou yo, gen de plis zouti enteresan nan .NET: PLinq (Linq2Parallel) ak klas Paralèl la. Premye a pwomèt ekzekisyon paralèl nan tout operasyon Linq sou fil miltip. Nimewo a nan fil ka konfigirasyon lè l sèvi avèk metòd ekstansyon WithDegreeOfParallelism. Malerezman, pi souvan PLinq nan mòd default li yo pa gen ase enfòmasyon sou entèn yo nan sous done ou a bay yon benefis vitès enpòtan, nan lòt men an, pri a nan eseye se trè ba: ou jis bezwen rele metòd la AsParallel anvan. chèn lan nan metòd Linq ak tès pèfòmans kouri. Anplis, li posib pou bay PLinq plis enfòmasyon sou nati sous done w yo lè l sèvi avèk mekanis Partitions yo. Ou ka li plis isit la и isit la.

Klas estatik Paralèl la bay metòd pou iterasyon atravè yon koleksyon Foreach an paralèl, egzekite yon bouk For, ak egzekite plizyè delege an paralèl Invoke. Egzekisyon fil aktyèl la ap sispann jiskaske kalkil yo fini. Nimewo a nan fil ka konfigirasyon lè w pase ParallelOptions kòm dènye agiman an. Ou kapab tou presize TaskScheduler ak CancellationToken lè l sèvi avèk opsyon.

Jwenn

Lè m 'te kòmanse ekri atik sa a ki baze sou materyèl yo nan rapò mwen an ak enfòmasyon ke mwen kolekte pandan travay mwen apre li, mwen pa t' espere ke ta gen anpil nan li. Kounye a, lè editè tèks kote m ap tape atik sa a ak repwoche di m paj 15 la ale, m ap rezime rezilta pwovizwa yo. Lòt ke trik nouvèl, API, zouti vizyèl ak enkonvenyans yo pral kouvri nan pwochen atik la.

Konklizyon:

  • Ou bezwen konnen zouti yo pou travay ak fil, asynchrony ak paralelis yo nan lòd yo sèvi ak resous yo nan òdinatè modèn.
  • .NET gen anpil zouti diferan pou rezon sa yo
  • Se pa tout nan yo parèt an menm tan, kidonk ou ka souvan jwenn eritaj yo, sepandan, gen fason yo konvèti ansyen APIs san anpil efò.
  • Travay ak fil nan .NET reprezante pa klas Thread ak ThreadPool
  • Metòd Thread.Abort, Thread.Interrupt, ak Win32 API TerminateThread yo danjere epi yo pa rekòmande pou itilize. Olye de sa, li pi bon pou itilize mekanis CancellationToken
  • Flow se yon resous ki gen anpil valè ak rezèv li limite. Sitiyasyon kote fil yo okipe ap tann evènman yo ta dwe evite. Pou sa li bon pou itilize klas TaskCompletionSource
  • Zouti .NET ki pi pwisan ak avanse pou travay ak paralelis ak asynchrony yo se Travay.
  • Operatè c# async/wait aplike konsèp datant ki pa bloke
  • Ou ka kontwole distribisyon Tasks atravè fil lè l sèvi avèk klas ki sòti nan TaskScheduler
  • Estrikti ValueTask ka itil nan optimize cho-chemen ak memwa-trafik
  • Fenèt Tasks ak Threads Visual Studio a bay anpil enfòmasyon itil pou debogaj kòd milti-threaded oswa kòd asynchrone.
  • PLinq se yon zouti fre, men li ka pa gen ase enfòmasyon sou sous done ou, men sa a ka ranje lè l sèvi avèk mekanis nan patisyon.
  • A kontinye…

Sous: www.habr.com

Add nouvo kòmantè