Faylasuufyo si Wanaagsan loo quudiyo ama Barnaamijyada NET ee Tartamaya

Faylasuufyo si Wanaagsan loo quudiyo ama Barnaamijyada NET ee Tartamaya

Aan aragno sida barnaamijyada isku midka ah iyo kuwa isbarbar socda ay uga shaqeeyaan .Net, annagoo adeegsanayna Dhibaatada Cunnada Filasoofyada tusaale ahaan. Qorshuhu waa kan, laga bilaabo isku-dubbaridka dunta / geeddi-socodka, ilaa qaabka jilaa (qaybaha soo socda). Maqaalku waxa uu faa'iido u yeelan karaa isbarashada koowaad ama si aad u cusboonaysiiso aqoontaada.

Waa maxay sababta gabi ahaanba? Transistor-yadu waxay gaadhaan cabbirkooda ugu yar, sharciga Moore wuxuu ku tiirsan yahay xaddidaadda xawaaraha iftiinka sidaas darteed kororka tirada ayaa lagu arkay, transistor badan ayaa la samayn karaa. Isla mar ahaantaana, qadarka xogta ayaa sii kordheysa, isticmaalayaashuna waxay filayaan jawaab degdeg ah nidaamyada. Xaaladdan oo kale, barnaamijka "caadiga ah", marka aan haysanno hal dun oo fulinaysa, hadda ma shaqeyneyso. Waxaad u baahan tahay inaad si uun u xalliso dhibaatada fulinta isku mar ama isku mar ah. Waxaa intaa dheer, dhibaatadani waxay ka jirtaa heerar kala duwan: heerka dunta, heerka hababka, heerka mashiinada shabakada (nidaamyada qaybsan). NET waxay leedahay teknoolojiyad tayo sare leh oo waqti la tijaabiyay si dhakhso ah oo hufan loo xalliyo dhibaatooyinkan oo kale.

Ujeeddo

Edsger Dijkstra wuxuu dhibaatadan u soo jeediyay ardaydiisa horaantii 1965. Qaabka la aasaasay waa sida soo socota. Waxaa jira tiro cayiman (badanaa shan) oo faylasuufyo ah iyo tiro isku mid ah oo fargeeto ah. Waxay fadhiistaan ​​miis wareegsan, oo fargeeto dhexdooda ah. Faylasuufyadu waxay ka cuni karaan saxanadooda cuntada aan dhammaadka lahayn, way fikiri karaan ama sugi karaan. Si aad u cunto faylasuuf, waxaad u baahan tahay inaad qaadato laba fargeeto (kan u dambeeya wuxuu la wadaagaa fargeetada kan ugu horreeya). Qaadista iyo dhigista fargeetada waa laba fal oo kala duwan. Faylasuufyadii oo dhan way aamuseen. Hawshu waa in la helo algorithm-ka noocan oo kale ah oo dhammaantood ay ku fikiri lahaayeen oo ay buuxsami lahaayeen xitaa 54 sano ka dib.

Marka hore, aan isku dayno inaan ku xallinno dhibaatadan iyada oo la adeegsanayo meel la wadaago. Fargeetooyinku waxay ku seexdaan miiska caadiga ah, faylasuufyaduna waxay si fudud u qaataan marka ay joogaan oo dib u dhigaan. Halkan waxaa ka jira dhibaatooyin la xidhiidha wada shaqaynta, goorta saxda ah ee la qaadanayo surebets? ka waran haddaan fargeeto jirin? iwm. Laakiin marka hore, aan bilowno faylasuufyada.

Si loo bilaabo dunta, waxaan isticmaalnaa barkada dunta dhexmarta Task.Run habka:

var cancelTokenSource = new CancellationTokenSource();
Action<int> create = (i) => RunPhilosopher(i, cancelTokenSource.Token);
for (int i = 0; i < philosophersAmount; i++) 
{
    int icopy = i;
    // ΠŸΠΎΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ Π·Π°Π΄Π°Ρ‡Ρƒ Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ ΠΏΡƒΠ»Π° ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ². ΠœΠ΅Ρ‚ΠΎΠ΄ RunDeadlock Π½Π΅ Π·Π°ΠΏΡƒΡΠΊΠ°Π΅Ρ‚ΡŒΡΡ 
    // сразу, Π° ΠΆΠ΄Π΅Ρ‚ своСго ΠΏΠΎΡ‚ΠΎΠΊΠ°. Асинхронный запуск.
    philosophers[i] = Task.Run(() => create(icopy), cancelTokenSource.Token);
}

Barkadda duntu waxa loo qaabeeyey in lagu wanaajiyo abuurista dunta iyo tirtirka Barkaddani waxay leedahay saf hawlo leh, CLR-gu waxay abuurtaa ama ka saartaa dunta iyadoo ku xidhan tirada hawlahan. Hal barkad oo loogu talagalay dhammaan AppDomains. Barkaddan waa in had iyo jeer la isticmaalo, sababtoo ah. Looma baahna inaad ku dhibto abuurista, tirtirida dunta, safkooda, iwm. Waa suurtogal iyada oo aan barkad la'aanteed, laakiin markaa waa inaad si toos ah u isticmaashaa ThreadTani waxay faa'iido u leedahay kiisaska marka aad u baahan tahay inaad bedesho mudnaanta dunta, marka aan samayno qaliin dheer, dunta hore, iwm.

In si kale loo dhigo, System.Threading.Tasks.Task fasalka waa isku mid Thread, laakiin leh dhammaan noocyada kala duwan ee ku habboon: kartida in ay socodsiiyaan hawl ka dib block of hawlaha kale, ka soo celiso hawlaha, si habboon u dhexgali, iyo in ka badan. iwm. Waxaa loo baahan yahay si ay u taageeraan dhismooyinka asynchronous-ku-saleysan, sonkorta syntactic ee sugitaanka hawlgallada IO. Dib ayaan uga hadli doonaa arrintan.

CancelationTokenSource halkan ayaa loo baahan yahay si duntu ay isu joojin karto calaamadda dunta wacitaanka.

Arrimaha isku xidhka

Faylasuufyadii la xannibay

Hagaag, waan ognahay sida loo abuuro dunta, aan isku dayno inaan qadeyno:

// ΠšΡ‚ΠΎ ΠΊΠ°ΠΊΠΈΠ΅ Π²ΠΈΠ»ΠΊΠΈ взял. К ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρƒ: 1 1 3 3 - 1ΠΉ ΠΈ 3ΠΉ взяли ΠΏΠ΅Ρ€Π²Ρ‹Π΅ Π΄Π²Π΅ ΠΏΠ°Ρ€Ρ‹.
private int[] forks = Enumerable.Repeat(0, philosophersAmount).ToArray();

// Π’ΠΎ ΠΆΠ΅, Ρ‡Ρ‚ΠΎ RunPhilosopher()
private void RunDeadlock(int i, CancellationToken token) 
{
    // Π–Π΄Π°Ρ‚ΡŒ Π²ΠΈΠ»ΠΊΡƒ, Π²Π·ΡΡ‚ΡŒ Π΅Ρ‘. Π­ΠΊΠ²ΠΈΠ²Π°Π»Π΅Π½Ρ‚Π½ΠΎ: 
    // while(true) 
    //     if forks[fork] == 0 
    //          forks[fork] = i+1
    //          break
    //     Thread.Sleep() ΠΈΠ»ΠΈ Yield() ΠΈΠ»ΠΈ SpinWait()
    void TakeFork(int fork) =>
        SpinWait.SpinUntil(() => 
            Interlocked.CompareExchange(ref forks[fork], i+1, 0) == 0);

    // Для простоты, Π½ΠΎ ΠΌΠΎΠΆΠ½ΠΎ с Interlocked.Exchange:
    void PutFork(int fork) => forks[fork] = 0;

    while (true)
    {
        TakeFork(Left(i));
        TakeFork(Right(i));
        eatenFood[i] = (eatenFood[i] + 1) % (int.MaxValue - 1);
        PutFork(Left(i));
        PutFork(Right(i));
        Think(i);

        // Π—Π°Π²Π΅Ρ€ΡˆΠΈΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρƒ ΠΏΠΎ-Ρ…ΠΎΡ€ΠΎΡˆΠ΅ΠΌΡƒ.
        token.ThrowIfCancellationRequested();
    }
}

Halkan waxaan marka hore isku daynaa inaan qaadno fargeeto bidix, ka dibna fargeetada midigta, haddii ay shaqeyso, ka dibna waan cuneynaa oo dib u celineynaa. Qaadashada hal fargeeto waa atomic, i.e. laba dunood isku mar ma wada qaadan karaan hal mar ( khaldan: marka hore waxay akhridaa in fargeeto xor yahay, kan labaadna - sidoo kale, kan hore ayaa qaata, kan labaadna wuu qaataa). Tan awgeed Interlocked.CompareExchange, kaas oo ay tahay in lagu fuliyo tilmaamaha processor-ka (TSL, XCHG), kaas oo quful xusuus ah akhriska iyo qorista isku xigxiga ee atomiga. SpinWaitna waxay u dhigantaa dhismaha while(true) Kaliya wax yar "sixir" - duntu waxay qaadataa processor-ka (Thread.SpinWait), laakiin mararka qaarkood waxay u wareejisaa xakamaynta dun kale (Thread.Yeild) ama seexdaa (Thread.Sleep).

Laakiin xalkani ma shaqaynayo, sababtoo ah socodka dhaqsaha badan (Aniga ahaan hal ilbiriqsi gudahood) waa la xannibay: dhammaan faylasuufyada waxay qaataan fargeeto bidix, laakiin ma aha mid sax ah. Qaabka fargeetada ayaa markaa leh qiimayaasha: 1 2 3 4 5.

Faylasuufyo si Wanaagsan loo quudiyo ama Barnaamijyada NET ee Tartamaya

Jaantuska, xidhidhiyaha dunta (deadlock). Cagaaran - dil, casaan - isku-dubarid, cawl - duntu waa huruddaa. Rhombuses-ku waxay muujinayaan wakhtiga bilawga Hawlaha.

Gaajada Faylasuufyada

Inkasta oo aan loo baahnayn in laga fekero gaar ahaan cunto badan, laakiin gaajadu waxay ka dhigtaa qof kasta inuu ka tanaasulo falsafada. Aan isku dayno inaan ku ekayno xaaladda gaajadda dunta ee dhibaatadeena. Gaajo waa marka duntu socoto, laakiin shaqo la'aan la'aanteed, si kale haddii loo dhigo, tani waa isla xannibaadda, kaliya hadda duntu ma hurdo, laakiin waxay si firfircoon u raadineysaa wax la cuno, laakiin ma jirto cunto. Si looga fogaado xannibaadda soo noqnoqda, waxaan dib u celin doonaa fargeetada haddii aynaan qaadan karin mid kale.

// Π’ΠΎ ΠΆΠ΅ Ρ‡Ρ‚ΠΎ ΠΈ Π² RunDeadlock, Π½ΠΎ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΊΠ»Π°Π΄Π΅ΠΌ Π²ΠΈΠ»ΠΊΡƒ Π½Π°Π·Π°Π΄ ΠΈ добавляСм ΠΏΠ»ΠΎΡ…ΠΈΡ… философов.
private void RunStarvation(int i, CancellationToken token)
{
    while (true)
    {
        bool hasTwoForks = false;
        var waitTime = TimeSpan.FromMilliseconds(50);
        // ΠŸΠ»ΠΎΡ…ΠΎΠΉ философов ΠΌΠΎΠΆΠ΅Ρ‚ ΡƒΠΆΠ΅ ΠΈΠΌΠ΅Ρ‚ΡŒ Π²ΠΈΠ»ΠΊΡƒ:
        bool hasLeft = forks[Left(i)] == i + 1;
        if (hasLeft || TakeFork(Left(i), i + 1, waitTime))
        {
            if (TakeFork(Right(i), i + 1, TimeSpan.Zero))
                hasTwoForks = true;
            else
                PutFork(Left(i)); // Иногда ΠΏΠ»ΠΎΡ…ΠΎΠΉ философ ΠΎΡ‚Π΄Π°Π΅Ρ‚ Π²ΠΈΠ»ΠΊΡƒ Π½Π°Π·Π°Π΄.
        } 
        if (!hasTwoForks)
        {
            if (token.IsCancellationRequested) break;
            continue;
        }
        eatenFood[i] = (eatenFood[i] + 1) % (int.MaxValue - 1);
        bool goodPhilosopher = i % 2 == 0;
        // А ΠΏΠ»ΠΎΡ…ΠΎΠΉ философ Π·Π°Π±Ρ‹Π²Π°Π΅Ρ‚ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚ΡŒ свою Π²ΠΈΠ»ΠΊΡƒ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎ:
        if (goodPhilosopher)
            PutFork(Left(i));
        // А Ссли ΠΈ ΠΏΡ€Π°Π²ΡƒΡŽ Π½Π΅ ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚, Ρ‚ΠΎ Ρ…ΠΎΡ€ΠΎΡˆΠΈΠ΅ Π±ΡƒΠ΄ΡƒΡ‚ Π²ΠΎΠΎΠ±Ρ‰Π΅ Π±Π΅Π· Π΅Π΄Ρ‹.
        PutFork(Right(i));

        Think(i);

        if (token.IsCancellationRequested)
            break;
    }
}

// Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠΆΠ½ΠΎ ΠΆΠ΄Π°Ρ‚ΡŒ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½ΠΎΠ΅ врСмя.
bool TakeFork(int fork, int philosopher, TimeSpan? waitTime = null)
{
    return SpinWait.SpinUntil(
        () => Interlocked.CompareExchange(ref forks[fork], philosopher, 0) == 0,
              waitTime ?? TimeSpan.FromMilliseconds(-1)
    );
}

Waxa muhiimka ah ee ku saabsan xeerkan waa in afartii faylasuuf laba ka mid ahi ay illoobaan inay fargeeto bidix dhigaan. Waxayna soo baxday inay cunaan cunto badan, halka qaar kalena ay bilaabaan inay gaajoodaan, inkastoo duntu ay leeyihiin mudnaan isku mid ah. Halkan si buuxda uma gaajoonayaan, sababtoo ah. faylasuufyada xun waxay dib u celiyaan fargeeto mararka qaarkood. Waxaa soo baxday in dadka wanaagsani ay cunaan qiyaastii 5 jeer wax ka yar kuwa xun. Markaa qalad yar oo ku jira koodka ayaa horseedaya hoos u dhaca waxqabadka. Waxa kale oo mudan in halkan lagu xuso in xaalad naadir ah ay suurtogal tahay marka dhammaan faylasuufyadu ay qaataan fargeeto bidix, mid sax ah ma jiro, bidix ayay dhigaan, sugaan, bidix mar kale qaataan, iwm. Xaaladdani sidoo kale waa gaajo, oo u badan sida xannibaadda. Waan ku guuldareystay inaan ku celiyo. Hoos waxaa ku yaal sawir xaalad ah oo laba faylasuuf ah oo xun ay labadoodaba fargeeto ka qaadeen oo laba wanaagsani ay gaajoonayaan.

Faylasuufyo si Wanaagsan loo quudiyo ama Barnaamijyada NET ee Tartamaya

Halkan waxaad ku arki kartaa in duntu ay soo toosaan mararka qaarkood oo ay isku dayaan in ay helaan kheyraadka. Laba ka mid ah afarta geesood waxba ma qabtaan (garaafka cagaaran ee sare).

Geeridii Faylasuuf

Hagaag, mushkilad kale oo hakad gelin karta casho sharafeedka faylasuufyada waa haddii mid iyaga ka mid ahi si lama filaan ah u dhinto isaga oo fargeeto gacmihiisa ku jira (oo ay sidaas ku aasi doonaan). Markaas deriska ayaa casho la'aan laga tegi doonaa. Waxaad la iman kartaa kood kood ah kiiskan laftaadu, tusaale ahaan, waa la tuuray NullReferenceException ka dib markii uu faylasuufku fargeetooyinka qaato. Iyo, jidka, ka reebanaanshaha lama qaban doono oo koodka wicitaanku ma qaban doono oo keliya (tan AppDomain.CurrentDomain.UnhandledException iyo iwm.). Sidaa darteed, khaladka-hawlgalayaasha ayaa looga baahan yahay dunta laftooda iyo joojinta si qurux badan.

Waiter

Hagaag, sideen ku xalin karnaa is-qab-qabsigan, gaajada, iyo dhibaatada dhimashada? Waxaan u ogolaan doonaa hal faylasuuf kaliya inuu gaaro fargeetada, ku darso ka saarista dunta meeshan. Sidee loo sameeyaa? Ka soo qaad in uu jiro mudalab ag fadhiya faylasuufyada kaas oo u fasaxaya faylasuuf kasta inuu fargeeto qaato. Sideen u samaynaa kabalyeerigan iyo sida faylasuufyadu u weydiin doonaan isaga, su'aaluhu waa kuwo xiiso leh.

Habka ugu fudud ayaa ah marka faylasuufyadu ay si joogto ah u waydiisanayaan kabalyeeriyada gelitaanka fargeetada. Kuwaas. Hadda faylasuufyadu ma sugi doonaan fargeeto dhow, laakiin sugaan ama weydii kabalyeeriyada. Marka hore, waxa aan u isticmaalnaa oo keliya Booska Isticmaalaha tan, dhexdeeda ma isticmaalno kala-goynta si aan uga wacno habab kasta oo kernel-ka ah (ku saabsan iyaga hoos).

Xalka booska isticmaalaha

Halkan waxa aynu ku samayn doonaa sidii aynu ku samayn jirnay fargeeto iyo laba faylasuuf, waxa aynu ku miiqnaa wareegga oo aynu sugi doonnaa. Laakiin hadda waxay noqon doontaa dhammaan faylasuufyada iyo, sida ay ahayd, hal fargeeto, i.e. waxa la odhan karaa kaliya faylasuufkii ka qaatay β€œfargeeto dahabka ah” ee kabalyeeriyada ayaa cuni doona. Tan waxaan u isticmaalnaa SpinLock.

private static SpinLock spinLock = new SpinLock();  // Наш "ΠΎΡ„ΠΈΡ†ΠΈΠ°Π½Ρ‚"
private void RunSpinLock(int i, CancellationToken token)
{
    while (true)
    {
        // Взаимная Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠ° Ρ‡Π΅Ρ€Π΅Π· busy waiting. Π’Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌ Π΄ΠΎ try, Ρ‡Ρ‚ΠΎΠ±Ρ‹
        // Π²Ρ‹Π±Ρ€Π°ΡΠΈΡ‚ΡŒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π² случаС ошибки Π² самом SpinLock.
        bool hasLock = false;
        spinLock.Enter(ref hasLock);
        try
        {
            // Π—Π΄Π΅ΡΡŒ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ΄ΠΈΠ½ ΠΏΠΎΡ‚ΠΎΠΊ (mutual exclusion).
            forks[Left(i)] = i + 1;  // Π‘Π΅Ρ€Π΅ΠΌ Π²ΠΈΠ»ΠΊΡƒ сразу, Π±Π΅Π· оТидания.
            forks[Right(i)] = i + 1;
            eatenFood[i] = (eatenFood[i] + 1) % (int.MaxValue - 1);
            forks[Left(i)] = 0;
            forks[Right(i)] = 0;
        }
        finally
        {
            if(hasLock) spinLock.Exit();  // ИзбСгаСм ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ со ΡΠΌΠ΅Ρ€Ρ‚ΡŒΡŽ философа.
        }

        Think(i);

        if (token.IsCancellationRequested)
            break;
    }
}

SpinLock Tani waa xannibaade, oo leh, qiyaas ahaan, isku mid while(true) { if (!lock) break; }, laakiin leh xitaa "sixir" ka badan inta ku jirta SpinWait (kaas oo halkaas lagu isticmaalo). Hadda wuu garanayaa sida loo tiriyo kuwa sugaya, in yar la seexiyo, iyo in ka badan. iwm. Guud ahaan, waxay sameeyaan wax kasta oo suurtagal ah si kor loogu qaado. Laakiin waa in aan xasuusannaa in tani ay weli tahay isla wareegga firfircoon ee cunaya agabka processor-ka oo sii qulqulaya, taas oo keeni karta gaajo haddii mid ka mid ah faylasuufiinta uu noqdo mid ka mudan kuwa kale, laakiin aan lahayn fargeeto dahab ah (Priority Inversion problem). . Sidaa darteed, waxaan u isticmaalnaa oo kaliya isbeddellada aadka u gaaban ee xusuusta la wadaago, iyada oo aan wax wicitaan ah oo dhinac saddexaad ah, quful buul leh, iyo waxyaabo kale oo la yaab leh.

Faylasuufyo si Wanaagsan loo quudiyo ama Barnaamijyada NET ee Tartamaya

U sawirida SpinLock. Durdurrada ayaa si joogto ah "dagaal ugu jira" fargeeto dahab ah. Waxaa jira guuldarooyin - shaxanka, aagga la doortay. Xudunta aan si buuxda looga faa'iidaysan: kaliya ilaa 2/3 afartan dunood.

Xalka kale ee halkan waa in la isticmaalo oo kaliya Interlocked.CompareExchange iyada oo la mid ah sugitaan firfircoon oo la mid ah sida ku cad koodhka sare (falasuufyada gaajaysan), laakiin tani, sidii horeba loo sheegay, waxay fikrad ahaan u horseedi kartaa xannibaad.

on Interlocked Waa in la ogaadaa in aysan jirin oo kaliya CompareExchange, laakiin sidoo kale habab kale oo atomikada loo akhriyo loona qoro. Iyo ku celcelinta isbeddelka, haddii dunta kale ay waqti u hesho isbeddelkeeda (akhri 1, akhri 2, qor 2, qor 1 waa mid xun), waxa loo isticmaali karaa isbeddello kakan oo hal qiime ah (Interlocked Anything pattern) .

Habka Kernel Solutions

Si aan uga fogaano in aan ku lumin kheyraadka wareegga, aan aragno sida aan u xannibi karno dunta. Si kale haddii loo dhigo, sii wadida tusaalaheena, aan aragno sida kabalyeerigu u seexiyo falsafada oo u toosiyo kaliya marka loo baahdo. Marka hore, aynu eegno sida tan loo sameeyo iyada oo loo marayo habka kernel ee nidaamka hawlgalka. Dhammaan dhismayaasha jira ayaa inta badan ka gaabiya kuwa ku jira booska isticmaalaha. Dhowr jeer ayaa ka gaabinaya, tusaale ahaan AutoResetEvent laga yaabaa in 53 jeer gaabis ah SpinLock [Richter]. Laakin iyaga oo kaashanaya, waxaad isku wadi kartaa hababka nidaamka oo dhan, la maareeyo iyo haddii kale.

Dhismaha aasaasiga ah ee halkan waa semaphore ay soo jeedisay Dijkstra nus qarni ka hor. Semaphore-ku waa, si fudud loo dhejiyaa, tiro togan oo nidaamku maamulo, iyo laba hawlgal oo lagu sameeyo, korodh iyo hoos u dhac. Haddii ay ku guuldareysato inay hoos u dhigto, eber, markaas dunta wacitaanka waa la xannibay. Marka nambarka lagu kordhiyo dun/nidaam kale oo firfircoon, markaas duntu waa la boodaa oo semaphore ayaa mar kale la dhimay lambarka la gudbay. Waxaa la qiyaasi karaa tareenada ku jira qoorta dhalo oo leh semaphore. NET waxay bixisaa dhowr dhismo oo leh shaqeyn isku mid ah: AutoResetEvent, ManualResetEvent, Mutex iyo naftayda Semaphore. Waan isticmaali doonaa AutoResetEvent, tani waa tan ugu fudud ee dhismayaashan: kaliya laba qiyam 0 iyo 1 (been, run). Habkeeda WaitOne() wuxuu xannibaa dunta wicitaanka haddii qiimihiisu ahaa 0, haddii 1, hoos u dhigaya 0 wuuna ka boodaa. Habka Set() kor u kaca ilaa 1 oo u ogolaada in hal adeege uu dhex maro, kaas oo mar kale hoos u dhigaya 0. Wuxuu u dhaqmaa sida leexashada tareenka dhulka hoostiisa mara.

Aynu xalka isku adkayno oo aynu u isticmaalno qufulka faylasuuf kasta, ee yaynaan hal mar wada isticmaalin. Kuwaas. hadda waxaa jiri kara dhowr faylasuuf hal mar, oo midna maaha. Laakiin waxaan mar kale xannibnaa gelitaanka miiska si aan si sax ah u, ka fogaano jinsiyadaha (xaaladda jinsiyadda), qaadashada surebets.

// Для блокирования ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ философа.
// Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅Ρ‚ΡΡ: new AutoResetEvent(true) для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ.
private AutoResetEvent[] philosopherEvents;

// Для доступа ΠΊ Π²ΠΈΠ»ΠΊΠ°ΠΌ / доступ ΠΊ столу.
private AutoResetEvent tableEvent = new AutoResetEvent(true);

// Π ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ философа.
public void Run(int i, CancellationToken token)
{
    while (true)
    {
        TakeForks(i); // Π–Π΄Π΅Ρ‚ Π²ΠΈΠ»ΠΊΠΈ.
        // ОбСд. ΠœΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΈ дольшС.
        eatenFood[i] = (eatenFood[i] + 1) % (int.MaxValue - 1);
        PutForks(i); // ΠžΡ‚Π΄Π°Ρ‚ΡŒ Π²ΠΈΠ»ΠΊΠΈ ΠΈ Ρ€Π°Π·Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ сосСдСй.
        Think(i);
        if (token.IsCancellationRequested) break;
    }
}

// ΠžΠΆΠΈΠ΄Π°Ρ‚ΡŒ Π²ΠΈΠ»ΠΊΠΈ Π² Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠ΅.
void TakeForks(int i)
{
    bool hasForks = false;
    while (!hasForks) // ΠŸΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ Π΅Ρ‰Π΅ Ρ€Π°Π· (Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠ° Π½Π΅ здСсь).
    {
        // Π˜ΡΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‰ΠΈΠΉ доступ ΠΊ столу, Π±Π΅Π· Π³ΠΎΠ½ΠΎΠΊ Π·Π° Π²ΠΈΠ»ΠΊΠ°ΠΌΠΈ.
        tableEvent.WaitOne();
        if (forks[Left(i)] == 0 && forks[Right(i)] == 0)
            forks[Left(i)] = forks[Right(i)] = i + 1;
        hasForks = forks[Left(i)] == i + 1 && forks[Right(i)] == i + 1;
        if (hasForks)
            // Π’Π΅ΠΏΠ΅Ρ€ΡŒ философ поСст, Π²Ρ‹ΠΉΠ΄Π΅Ρ‚ ΠΈΠ· Ρ†ΠΈΠΊΠ»Π°. Если Set 
            // Π²Ρ‹Π·Π²Π°Π½ Π΄Π²Π°ΠΆΠ΄Ρ‹, Ρ‚ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ true.
            philosopherEvents[i].Set();
        // Π Π°Π·Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΎΠΆΠΈΠ΄Π°ΡŽΡ‰Π΅Π³ΠΎ. ПослС Π½Π΅Π³ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ tableEvent Π² false.
        tableEvent.Set(); 
        // Если ΠΈΠΌΠ΅Π΅Ρ‚ true, Π½Π΅ блокируСтся, Π° Ссли false, Ρ‚ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΠΆΠ΄Π°Ρ‚ΡŒ Set ΠΎΡ‚ сосСда.
        philosopherEvents[i].WaitOne();
    }
}

// ΠžΡ‚Π΄Π°Ρ‚ΡŒ Π²ΠΈΠ»ΠΊΠΈ ΠΈ Ρ€Π°Π·Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ сосСдСй.
void PutForks(int i)
{
    tableEvent.WaitOne(); // Π‘Π΅Π· Π³ΠΎΠ½ΠΎΠΊ Π·Π° Π²ΠΈΠ»ΠΊΠ°ΠΌΠΈ.
    forks[Left(i)] = 0;
    // ΠŸΡ€ΠΎΠ±ΡƒΠ΄ΠΈΡ‚ΡŒ Π»Π΅Π²ΠΎΠ³ΠΎ, Π° ΠΏΠΎΡ‚ΠΎΠΌ ΠΈ ΠΏΡ€Π°Π²ΠΎΠ³ΠΎ сосСда, Π»ΠΈΠ±ΠΎ AutoResetEvent Π² true.
    philosopherEvents[LeftPhilosopher(i)].Set();
    forks[Right(i)] = 0;
    philosopherEvents[RightPhilosopher(i)].Set();
    tableEvent.Set();
}

Si aad u fahamto waxa halkan ka dhacaya, tixgeli kiiska marka faylasuufku ku guuldareystay inuu qaato fargeeto, ka dibna ficilladiisu waxay noqon doonaan sida soo socota. Wuxuu sugayaa gelitaanka miiska. Markuu helay, wuxuu isku dayaa inuu qaato fargeeto. Ma shaqayn Waxay siinaysaa gelitaanka miiska (iska-saarid labada dhinac ah). Oo uu dhaafo "rogiddiisa" (AutoResetEvent) (horta way furan yihiin). Waxay mar kale gelaysaa wareegga, sababtoo ah fargeeto ma haysto. Waxa uu isku dayaa in uu kaxeeyo oo uu istaago "rogiddiisa". Qaar ka mid ah deris nasiib badan oo midigta ama bidixda ah, markay dhammeeyeen cunista, waxay furaan falsafadayada, "furitaanka leexashadiisa." Faylasuufkeennu wuu dhaafaa (oo gadaashiisa ayuu xidhaa) mar labaad. Mar saddexaad ayuu isku dayay inuu fargeetooyinka qaato. Nasiib wacan. Oo wuxuu dhaafaa leexashadiisa si uu u casheeyo.

Marka ay jiraan khaladaad bakhtiyaa-nasiib ah oo ku jira koodhkan (had iyo jeer way jiraan), tusaale ahaan, deriska ayaa si khaldan loo cayimay ama shay la mid ah ayaa la abuuray AutoResetEvent dhammaan (Enumerable.Repeat), ka dibna falsafada ayaa sugi doona horumarinta, sababtoo ah Helitaanka khaladaadka koodhkan oo kale waa hawl adag. Dhibaatada kale ee xalkani waa iyada oo aan dammaanad qaadin in faylasuufka qaar uusan gaajoon doonin.

Xalka Isku-dhafka ah

Waxaan eegnay laba hab oo loo wajaho wakhtiga, marka aan ku jirno habka isticmaalaha iyo loop, iyo marka aan xannibno dunta dhexda kernel-ka. Habka kowaad wuxuu u fiican yahay quful gaaban, kan labaadna kuwa dheer. Badanaa waxaa lagama maarmaan ah in marka hore si kooban loo sugo doorsoomayaasha si uu isu beddelo wareegtada, ka dibna la xiro dunta marka sugitaanku dheer yahay. Habkan waxaa lagu fuliyaa waxa loogu yeero. qaababka isku-dhafan. Halkan waxaa ah dhismayaal la mid ah qaabka kernel-ka, laakiin hadda oo leh habka isticmaalaha: SemaphorSlim, ManualResetEventSlim iwm Naqshadaynta ugu caansan halkan waa Monitor, sababtoo ah C # waxaa ku yaal mid la yaqaan lock syntax. Monitor Kani waa semaphore la mid ah oo leh qiimaha ugu sarreeya ee 1 (mutex), laakiin leh taageerada sugitaanka wareegga, dib-u-celinta, qaabka Isbeddelka Xaaladda (wax badan oo hoosta ku yaal), iwm. Aan eegno xalka iyada.

// БпрячСм ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ для ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€Π° ΠΎΡ‚ всСх, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π±Π΅Π· Π΄Π΅Π΄Π»ΠΎΠΊΠΎΠ².
private readonly object _lock = new object();
// ВрСмя оТидания ΠΏΠΎΡ‚ΠΎΠΊΠ°.
private DateTime?[] _waitTimes = new DateTime?[philosophersAmount];

public void Run(int i, CancellationToken token)
{
    while (true)
    {
        TakeForks(i);
        eatenFood[i] = (eatenFood[i] + 1) % (int.MaxValue - 1);
        PutForks(i);
        Think(i);
        if (token.IsCancellationRequested) break;
    }
}

// НашС слоТноС условиС для Condition Variable ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½Π°.
bool CanIEat(int i)
{
    // Если Π΅ΡΡ‚ΡŒ Π²ΠΈΠ»ΠΊΠΈ:
    if (forks[Left(i)] != 0 && forks[Right(i)] != 0)
        return false;
    var now = DateTime.Now;
    // ΠœΠΎΠΆΠ΅Ρ‚, Ссли сосСди Π½Π΅ Π±ΠΎΠ»Π΅Π΅ Π³ΠΎΠ»ΠΎΠ΄Π½Ρ‹Π΅, Ρ‡Π΅ΠΌ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ.
    foreach(var p in new int[] {LeftPhilosopher(i), RightPhilosopher(i)})
        if (_waitTimes[p] != null && now - _waitTimes[p] > now - _waitTimes[i])
            return false;
    return true;
}

void TakeForks(int i)
{
    // Π—Π°ΠΉΡ‚ΠΈ Π² ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€. Π’ΠΎ ΠΆΠ΅ самоС: lock(_lock) {..}.
    // Π’Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌ Π²Π½Π΅ try, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠ΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π²Ρ‹Π±Ρ€Π°ΡΡ‹Π²Π°Π»ΠΎΡΡŒ Π²Ρ‹ΡˆΠ΅.
    bool lockTaken = false;
    Monitor.Enter(_lock, ref lockTaken);
    try
    {
        _waitTimes[i] = DateTime.Now;
        // Condition Variable ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½. ОсвобоТдаСм Π»ΠΎΠΊ, Ссли Π½Π΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½Π½ΠΎ 
        // слоТноС условиС. И ΠΆΠ΄Π΅ΠΌ ΠΏΠΎΠΊΠ° ΠΊΡ‚ΠΎ-Π½ΠΈΠ±ΡƒΠ΄ΡŒ сдСлаСт Pulse / PulseAll.
        while (!CanIEat(i))
            Monitor.Wait(_lock); 
        forks[Left(i)] = i + 1;
        forks[Right(i)] = i + 1;
        _waitTimes[i] = null;
    }
    finally
    {
        if (lockTaken) Monitor.Exit(_lock);
    }
}

void PutForks(int i)
{
    // Во ТС самоС: lock (_lock) {..}.
    bool lockTaken = false;
    Monitor.Enter(_lock, ref lockTaken);
    try
    {
        forks[Left(i)] = 0;
        forks[Right(i)] = 0;
        // ΠžΡΠ²ΠΎΠ±ΠΎΠ΄ΠΈΡ‚ΡŒ всС ΠΏΠΎΡ‚ΠΎΠΊΠΈ Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ ΠŸΠžΠ‘Π›Π• Π²Ρ‹Π·ΠΎΠ²Π° Monitor.Exit.
        Monitor.PulseAll(_lock); 
    }
    finally
    {
        if (lockTaken) Monitor.Exit(_lock);
    }
}

Halkan waxa aanu mar kale xannibnay miiska oo dhan si loo galo fargeetada, laakiin hadda waanu furaynaa dhammaan dunta hal mar, ee maaha deriska marka qof dhameeyo wax cunista. Kuwaas. marka hore qof baa wax cuna oo jaarka ka xidha, marka uu kan dhameeyo, balse doonaya in uu isla markiiba wax cuno, waxa uu galaa xannibaad iyo toosin deriskiisa, sababtoo ah. waqtigeeda sugitaanku waa ka yar yahay.

Tani waa sida aan uga fogaanno dhimista iyo gaajada faylasuufka qaarkood. Waxaan u isticmaalnaa loop sugitaan gaaban oo aan xirno dunta muddo dheer. In qof kasta la furo hal mar way ka gaabinaysaa haddii deriska la xannibay, sida xalka AutoResetEvent, laakiin faraqa waa inuusan noqon mid weyn, sababtoo ah duntu waa inay ku jiraan qaabka isticmaalaha marka hore.

Π£ lock syntax waxay leedahay yaabab xun. Ku talinaynaa in la isticmaalo Monitor toos ah [Richter] [Eric Lippert]. Mid ka mid ah waa taas lock had iyo jeer ka baxsan Monitor, xitaa haddii ay jirto wax ka reeban, taas oo ay dhacdo dun kale ayaa bedeli karta xaaladda xusuusta la wadaago. Xaaladahan oo kale, inta badan way fiican tahay in la tago xannibaadda ama si badbaado leh loo joojiyo barnaamijka. La yaab kale ayaa ah in Monitor uu isticmaalo baloogyada isku xidhkaSyncBlock), kuwaas oo ku jira dhammaan walxaha. Sidaa darteed, haddii shay aan habboonayn la doorto, waxaad si fudud u heli kartaa xannibaad (tusaale ahaan, haddii aad ku xirto xadhig gudaha ah). Waxaan u isticmaalnaa shayga qarsoon ee had iyo jeer tan.

Qaabka Isbeddelka ah ee Xaaladda wuxuu kuu oggolaanayaa inaad si kooban u fuliso rajada xaalad adag. Gudaha NET, waa mid aan dhamaystirnayn, fikradayda, sababtoo ah aragti ahaan, waa in ay jiraan dhowr saf oo doorsoomayaal ah (sida ku jirta Posix Threads), oo aan ahayn hal lok. Markaa mid ka mid ah ayaa u samayn kara dhammaan faylasuufyada. Laakiin xitaa foomkan, waxay kuu ogolaaneysaa inaad yareyso koodka.

faylasuufyo badan ama async / await

Hagaag, hadda waxaan si wax ku ool ah u xannibi karnaa dunta. Laakiin ka waran haddii aan haysano faylasuufyo badan? 100? 10000? Tusaale ahaan, waxa aanu helnay 100000 oo codsiyo serverka shabakada. Waxay noqon doontaa kor si loo abuuro dun codsi kasta, sababtoo ah maro badan oo isku mid ah kuma socon doonaan. Waxa kaliya oo socon doona inta ugu badan ee ay jiraan asal macquul ah (waxaan haystaa 4). Oo cid kasta oo kale waxay qaadan doontaa uun kheyraadka. Mid ka mid ah xalinta dhibaatadan waa qaabka async / sugitaanka. Fikradda ayaa ah in shaqadu aysan qaban dunta haddii ay u baahan tahay inay sugto wax sii socda. Oo marka ay wax qabato, waxay dib u bilaabataa fulinteeda (laakin ma aha qasab in isla dunta la mid ah!). Xaaladeena, waxaan sugi doonaa fargeetada.

SemaphoreSlim ayaa tan u leh WaitAsync() habka. Halkan waxaa ah hirgelinta iyadoo la isticmaalayo qaabkan.

// Запуск Ρ‚Π°ΠΊΠΎΠΉ ΠΆΠ΅, ΠΊΠ°ΠΊ Ρ€Π°Π½ΡŒΡˆΠ΅. Π“Π΄Π΅-Π½ΠΈΠ±ΡƒΠ΄ΡŒ Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ΅:
Task.Run(() => Run(i, cancelTokenSource.Token));

// Запуск философа.
// ΠšΠ»ΡŽΡ‡Π΅Π²ΠΎΠ΅ слово async -- компилятор транслируСт этот ΠΌΠ΅Ρ‚ΠΎΡ‚ Π² асинхронный.
public async Task Run(int i, CancellationToken token)
{
    while (true)
    {
        // await -- Π±ΡƒΠ΄Π΅ΠΌ ΠΎΠΆΠΈΠ΄Π°Ρ‚ΡŒ ΠΊΠ°ΠΊΠΎΠ³ΠΎ-Ρ‚ΠΎ события.
        await TakeForks(i);
        // ПослС await, ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Π² Π΄Ρ€ΡƒΠ³ΠΎΠΌ ΠΏΠΎΡ‚ΠΎΠΊΠ΅.
        eatenFood[i] = (eatenFood[i] + 1) % (int.MaxValue - 1);
        // ΠœΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ нСсколько событий для оТидания.
        await PutForks(i);

        Think(i);

        if (token.IsCancellationRequested) break;
    }
}

async Task TakeForks(int i)
{
    bool hasForks = false;
    while (!hasForks)
    {
        // Π’Π·Π°ΠΈΠΌΠΎΠΈΡΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‰ΠΈΠΉ доступ ΠΊ столу:
        await _tableSemaphore.WaitAsync();
        if (forks[Left(i)] == 0 && forks[Right(i)] == 0)
        {
            forks[Left(i)] = i+1;
            forks[Right(i)] = i+1;
            hasForks = true;
        }
        _tableSemaphore.Release();
        // Π‘ΡƒΠ΄Π΅ΠΌ ΠΎΠΆΠΈΠ΄Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ сосСд ΠΏΠΎΠ»ΠΎΠΆΠΈΠ» Π²ΠΈΠ»ΠΊΠΈ:
        if (!hasForks)
            await _philosopherSemaphores[i].WaitAsync();
    }
}

// Π–Π΄Π΅ΠΌ доступа ΠΊ столу ΠΈ ΠΊΠ»Π°Π΄Π΅ΠΌ Π²ΠΈΠ»ΠΊΠΈ.
async Task PutForks(int i)
{
    await _tableSemaphore.WaitAsync();
    forks[Left(i)] = 0;
    // "ΠŸΡ€ΠΎΠ±ΡƒΠ΄ΠΈΡ‚ΡŒ" сосСдСй, Ссли ΠΎΠ½ΠΈ "спали".
    _philosopherSemaphores[LeftPhilosopher(i)].Release();
    forks[Right(i)] = 0;
    _philosopherSemaphores[RightPhilosopher(i)].Release();
    _tableSemaphore.Release();
}

Habka leh async / await waxa loo turjumay mishiin dawladeed qalafsan oo isla markiiba soo celisa gudaha Task. Iyada oo loo marayo, waxaad sugi kartaa dhamaystirka habka, tirtiri kartaa, iyo wax kasta oo kale oo aad ku samayn karto Hawsha. Habka dhexdiisa, mashiinka gobolka ayaa xakameynaya fulinta. Guntii iyo gabagabadii, haddii aanay dib u dhac ku iman, fulintu waa isku mid, haddii ay jirtona dunta ayaa la sii daayaa. Si loo fahmo tan, way fiicantahay in la eego mishiinka gobolka. Waxaad ka abuuri kartaa silsilado kuwaas async / await hababka.

Aynu tijaabino. Shaqada 100 falsafadood ee mishiinka leh 4 nooc oo macquul ah, 8 ilbiriqsi. Xalka hore ee Monitor-ka ayaa kaliya ku socday 4-tii dun ee ugu horreysay inta soo hartayna ma aysan soconin gabi ahaanba. Mid kasta oo ka mid ah 4-daan xadhig waxa uu ahaa mid aan shaqayn ilaa 2ms. Oo xalka async / sugitaanku wuxuu socday dhammaan 100, iyadoo celcelis ahaan sugitaanka 6.8 ilbiriqsi kasta. Dabcan, nidaamyada dhabta ah, shaqo la'aanta 6 ilbiriqsi waa wax aan la aqbali karin waxaana fiican in aan la socodsiin codsiyo badan oo sidan oo kale ah. Xalka Monitor wuxuu noqday mid aan la miisaami karin gabi ahaanba.

gunaanad

Sida aad ka arki karto tusaalahan yar yar, .NET waxa ay taageertaa dhismooyin badan oo isku xidhid ah. Si kastaba ha ahaatee, mar walba ma cadda sida loo isticmaalo. Waxaan rajeynayaa in maqaalkani uu waxtar lahaa. Hadda, tani waa dhamaadka, laakiin weli waxyaabo badan oo xiiso leh ayaa hadhay, tusaale ahaan, ururinta dunta-ammaanka ah, TPL Dataflow, barnaamijka falcelinta, Moodeelka Ganacsiga Software, iwm.

Ilaha

Source: www.habr.com

Add a comment