IiFilosofi ezondliwe kakuhle okanye uKhuphiswano lwe-.NET Programming

IiFilosofi ezondliwe kakuhle okanye uKhuphiswano lwe-.NET Programming

Makhe sibone ukuba udweliso lwenkqubo oluhambelanayo lusebenza njani kwi-.Net, usebenzisa iPhilosophers Dining Problem njengomzekelo. Isicwangciso yilesi, ukusuka kungqamaniso lwemicu / iinkqubo, kwimodeli yomdlali (kwezi nxalenye zilandelayo). Eli nqaku linokuba luncedo kumntu omaziyo okanye ukuhlaziya ulwazi lwakho.

Kutheni usenza nje? I-Transistors ifikelela kubukhulu babo obuncinci, umthetho kaMoore uxhomekeke ekunciphiseni isantya sokukhanya kwaye ngoko ke ukunyuka kubonwa kwinani, ii-transistors ezininzi zingenziwa. Ngexesha elifanayo, inani ledatha likhula, kwaye abasebenzisi balindele impendulo ngokukhawuleza kwiinkqubo. Kwimeko enjalo, inkqubo "eqhelekileyo", xa sinentambo enye yokuqhuba, ayisasebenzi. Kufuneka uyisombulule ingxaki yokwenziwa ngaxeshanye okanye ngaxeshanye. Ngaphezu koko, le ngxaki ikhona kumanqanaba ahlukeneyo: kwinqanaba leentambo, kwinqanaba leenkqubo, kwinqanaba loomatshini kwinethiwekhi (iinkqubo ezisasazwayo). I-NET inomgangatho ophezulu, itekhnoloji evavanywe ixesha lokusombulula ngokukhawuleza nangokufanelekileyo iingxaki ezinjalo.

Injongo

U-Edsger Dijkstra ubeke le ngxaki kubafundi bakhe kwangoko ngo-1965. Uqulunqo olusekiweyo lulandelayo. Kukho inani elithile (elihlala lihlanu) lefilosofi kunye nenani elifanayo leefolokhwe. Bahlala kwitafile engqukuva, kukho iifolokhwe phakathi kwabo. Iifilosofi zinokutya kwiipleyiti zabo zokutya okungapheliyo, ukucinga okanye ukulinda. Ukutya ifilosofi, kufuneka uthathe iifolokhwe ezimbini (owokugqibela wabelana ngefoloko kunye neyokuqala). Ukuchola nokubeka phantsi ifolokhwe zizenzo ezimbini ezahlukeneyo. Zonke iintanda-bulumko zithe cwaka. Umsebenzi kukufumana i-algorithm enje ukuba bonke baya kucinga kwaye bazalise emva kweminyaka engama-54.

Okokuqala, makhe sizame ukusombulula le ngxaki ngokusebenzisa indawo ekwabelwana ngayo. Iifolokhwe zilele kwitafile eqhelekileyo kwaye iifilosofi zithatha nje xa zikhona kwaye zibuyisele. Apha kukho iingxaki ngongqamaniso, nini kanye kanye ukuthatha surebets? kuthekani ukuba akukho folokhwe? etc. Kodwa kuqala, makhe siqale iintanda-bulumko.

Ukuqala imisonto, sisebenzisa idama lemisonto Task.Run indlela:

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

Iqula lemisonto yenzelwe ukukhulisa indalo kunye nokucima imisonto. Eli dama linomgca kunye nemisebenzi kwaye i-CLR idala okanye isuse imicu ngokuxhomekeke kwinani lale misebenzi. Iqula elinye kuzo zonke i-AppDomains. Eli dama kufuneka lisetyenziswe phantse rhoqo, kuba. akukho mfuneko yakuzihlupha ngokudala, ukucima imisonto, imigca yabo, njalo njalo. Kuyenzeka ngaphandle kwedama, kodwa ke kufuneka uyisebenzise ngqo. Thread, oku kuluncedo kumatyala xa ufuna ukutshintsha umba ophambili womsonto, xa sinomsebenzi omde, kumsonto ongePhambili, njl.

Ngamanye amazwi, System.Threading.Tasks.Task iklasi iyafana Thread, kodwa ngazo zonke iintlobo eziluncedo: ukukwazi ukuqhuba umsebenzi emva kwebhloko yeminye imisebenzi, ukuyibuyisela kwimisebenzi, ukuyiphazamisa ngokufanelekileyo, kunye nokunye. njl. Ziyafuneka ukuxhasa i-async / ilindele ulwakhiwo (Ipateni esekwe kuMsebenzi weAsynchronous, iswekile elungelelanisiweyo yokulinda imisebenzi ye-IO). Siza kuthetha ngale nto kamva.

CancelationTokenSource apha iyafuneka ukuze umsonto uziphelise ngokwawo kumqondiso womsonto wokufowuna.

Sync Imiba

Izithandi zobulumko ezivaliweyo

Kulungile, siyayazi indlela yokwenza imisonto, makhe sizame ukuba nesidlo sasemini:

// ΠšΡ‚ΠΎ ΠΊΠ°ΠΊΠΈΠ΅ Π²ΠΈΠ»ΠΊΠΈ взял. К ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρƒ: 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();
    }
}

Apha siqala sizama ukuthatha ifolokhwe ekhohlo, kwaye emva koko ifoloko elungileyo, kwaye ukuba iyasebenza, ngoko sidla kwaye sibuyisele. Ukuthatha ifolokhwe enye i-athomu, okt. imicu emibini ayikwazi ukuthatha enye ngexesha elinye (engalunganga: eyokuqala ifunda ukuba ifolokhwe ikhululekile, eyesibini - nayo, eyokuqala ithatha, eyesibini ithatha). Yale Interlocked.CompareExchange, ekufuneka iphunyezwe ngomyalelo weprosesa (TSL, XCHG), evala isiqwenga sememori yokufunda nokubhala ngokulandelelana kweathom. Kwaye iSpinWait ilingana nolwakhiwo while(true) kuphela "ngomlingo" omncinci - umsonto uthatha iprosesa (Thread.SpinWait), kodwa ngamanye amaxesha idlulisela ulawulo komnye umsonto (Thread.Yeild) okanye uyalala (Thread.Sleep).

Kodwa esi sisombululo asisebenzi, kuba ukuhamba ngokukhawuleza (kum ngaphakathi komzuzwana) kuvaliwe: zonke iintanda-bulumko zithatha ifolokhwe yazo yasekhohlo, kodwa hayi ekunene. Uluhlu lwefolokhwe ngoko lunamaxabiso: 1 2 3 4 5.

IiFilosofi ezondliwe kakuhle okanye uKhuphiswano lwe-.NET Programming

Kumzobo, ukuvimba imisonto (i-deadlock). Uhlaza - ukubulawa, obomvu - ukuvumelanisa, i-gray - intambo ilele. I-rhombuses ibonisa ixesha lokuqala leMisebenzi.

Indlala yezithandi zobulumko

Nangona kungeyomfuneko ukuba ucinge ngokutya okuninzi, kodwa indlala yenza ukuba nabani na ayeke ifilosofi. Masizame ukulinganisa imeko yendlala yemisonto kwingxaki yethu. Indlala kuxa intambo isebenza, kodwa ngaphandle komsebenzi obalulekileyo, ngamanye amazwi, oku kufana ne-deadlock, kuphela ngoku umsonto awulali, kodwa ujonge ngenkuthalo into yokutya, kodwa akukho kutya. Ukuthintela ukuvaleka rhoqo, siya kuyibuyisela ifolokhwe ukuba asikwazanga ukuthatha enye.

// Π’ΠΎ ΠΆΠ΅ Ρ‡Ρ‚ΠΎ ΠΈ Π² 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)
    );
}

Into ebalulekileyo malunga nale khowudi kukuba ezimbini zefilosofi ezine zilibale ukubeka phantsi ifolokhwe yazo yasekhohlo. Kwaye kuvela ukuba batya ukutya okuninzi, ngelixa abanye beqala ukulamba, nangona imicu ibaluleke kakhulu. Apha abalambanga ngokupheleleyo, kuba. izithandi zobulumko ezimbi babeka iifolokhwe zabo emva ngamanye amaxesha. Kuvela ukuba abantu abalungileyo badla malunga namaxesha angama-5 ngaphantsi kokubi. Ngoko impazamo encinci kwikhowudi ikhokelela ekuweni kokusebenza. Kwakhona kuyafaneleka ukuba uqaphele apha ukuba imeko enqabileyo inokwenzeka xa zonke iifilosofi zithatha imfoloko ekhohlo, akukho kunene, babeka ngakwesobunxele, balinde, bathathe ekhohlo kwakhona, njl njl. Le meko ikwayindlala, ifana ne-deadlock. Ndisilele ukuyiphinda. Ngezantsi umfanekiso wemeko apho iifilosofi ezimbini ezimbi zithathe iifolokhwe zombini kunye nezimbini ezilungileyo zilambile.

IiFilosofi ezondliwe kakuhle okanye uKhuphiswano lwe-.NET Programming

Apha unokubona ukuba imisonto ivuka ngamanye amaxesha kwaye uzame ukufumana isibonelelo. Izibini ezimbini kwezine azenzi nto (igrafu eluhlaza ngasentla).

Ukufa kweSofilosofi

Ewe, enye ingxaki enokuphazamisa isidlo esizukileyo sefilosofi ukuba omnye wabo uyafa ngokukhawuleza ephethe iifolokhwe ezandleni zakhe (kwaye baya kumngcwaba ngolo hlobo). Emva koko abamelwane baya kushiywa ngaphandle kwesidlo. Unokuza nomzekelo wekhowudi yeli tyala ngokwakho, umzekelo, uphoswe ngaphandle NullReferenceException emva kokuba isithandi sobulumko sithathe iifolokhwe. Kwaye, ngendlela, okungafaniyo akuyi kuphathwa kwaye ikhowudi yokufowuna ayiyi kuyifumana nje (kule nto AppDomain.CurrentDomain.UnhandledException kunye njl.). Ke ngoko, abaphangi beziphoso bayafuneka kwimisonto ngokwayo kunye nokupheliswa okuhle.

Umlindi

Kulungile, siyisombulula njani le ngxaki, indlala, kunye nengxaki yokufa? Siza kuvumela ifilosofi enye kuphela ukuba ifikelele kwiifolokhwe, yongeza ukukhutshelwa ngaphandle kwemisonto kule ndawo. Ukwenza njani? Masithi kukho i-weyitala ecaleni kweentanda-bulumko enika imvume kuyo nayiphi na intanda-bulumko ukuba ithathe iifolokhwe. Siyenza njani lo mlindi kunye nendlela iifilosofi ziya kumbuza ngayo, imibuzo inomdla.

Eyona ndlela ilula kuxa iintanda-bulumko ziya kuhlala zicela umncedisi ukuba afikelele kwiifolokhwe. Ezo. ngoku iintanda-bulumko aziyi kulinda ifolokhwe ekufutshane, kodwa linda okanye ubuze umncedisi. Ekuqaleni, sisebenzisa kuphela iSithuba soMsebenzisi kule nto, kuyo asisebenzisi ukuphazamiseka ukubiza naziphi na iinkqubo ezivela kwi-kernel (malunga nazo ngezantsi).

Izisombululo kwindawo yomsebenzisi

Apha siya kwenza okufanayo njengoko sasiqhele ukwenza ngefolokhwe enye kunye neefilosofi ezimbini, siya kujikeleza kumjikelo kwaye silinde. Kodwa ngoku kuya kuba zonke iifilosofi kwaye, njengokuba kunjalo, ifolokhwe enye kuphela, i.e. kunokuthiwa kuphela isithandi sobulumko esathatha le "folokhwe yegolide" kwi-weyitara iya kudla. Kule nto sisebenzisa iSpinLock.

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 lo ngumqobo, kunye, ngokurhabaxa, into enye while(true) { if (!lock) break; }, kodwa "ngomlingo" ngakumbi kunangaphakathi SpinWait (esetyenziswa apho). Ngoku uyakwazi ukubala abo balindileyo, abalalise kancinane, nangaphezulu. njl. Ngokubanzi, yenza konke okusemandleni ukukhulisa. Kodwa kufuneka sikhumbule ukuba oku kusengumjikelezo ofanayo osebenzayo odla izixhobo zeprosesa kwaye ugcine ukuhamba, oku kunokukhokelela kwindlala ukuba enye yefilosofi iba yinto ephambili kunabanye, kodwa ingenayo ifolokhwe yegolide (ingxaki ye-Priority Inversion) . Ke ngoko, siyisebenzisela kuphela utshintsho olufutshane kakhulu kwimemori ekwabelwana ngayo, ngaphandle kweefowuni zomntu wesithathu, izitshixo ezifakwe kwindlwane, kunye nezinye izinto ezimangalisayo.

IiFilosofi ezondliwe kakuhle okanye uKhuphiswano lwe-.NET Programming

Umzobo we SpinLock. Imilambo ihlala "isilwela" ifolokhwe yegolide. Kukho ukungaphumeleli - kumzobo, indawo ekhethiweyo. Ii-cores azisetyenziswanga ngokupheleleyo: kuphela malunga ne-2/3 ngale misonto emine.

Esinye isisombululo apha siya kuba kukusebenzisa kuphela Interlocked.CompareExchange ngokulinda okufanayo okusebenzayo njengoko kubonisiwe kwikhowudi engentla (kwiintanda-bulumko ezilambileyo), kodwa oku, njengoko sele kuthethiwe, kunokukhokelela ekuthinteleni.

phezu Interlocked Kufuneka kuqatshelwe ukuba akukho kuphela CompareExchange, kodwa kunye nezinye iindlela zokufunda NOKUBHALA ngeathom. Kwaye ngokuphindaphinda utshintsho, xa kwenzeka enye intambo inexesha lokwenza utshintsho (funda 1, funda 2, bhala 2, bhala i-1 imbi), ingasetyenziselwa utshintsho oluyinkimbinkimbi kwixabiso elilodwa (I-Interlocked Nantoni na iphethini) .

Izisombululo zeModi yeKernel

Ukunqanda ukuchitha izibonelelo kwi-loop, makhe sibone ukuba singawuvala njani umsonto. Ngamanye amazwi, ukuqhubela phambili nomzekelo wethu, makhe sibone indlela i-weyitala elala ngayo intanda-bulumko kwaye imvuse kuphela xa kuyimfuneko. Okokuqala, makhe sijonge indlela yokwenza oku ngokusebenzisa imowudi ye-kernel yenkqubo yokusebenza. Zonke izakhiwo apho zihlala zicotha kunezo zikwindawo yomsebenzisi. Amaxesha amaninzi acothayo, umzekelo AutoResetEvent mhlawumbi 53 amaxesha kancinci SpinLock [Ubutyebi]. Kodwa ngoncedo lwabo, unokulungelelanisa iinkqubo kuyo yonke inkqubo, elawulwa okanye hayi.

Ulwakhiwo olusisiseko apha sisimaphore esicetyiswe nguDijkstra ngaphezulu kwesiqingatha senkulungwane eyadlulayo. I-semaphore, ngokulula, yinani elipheleleyo elichanekileyo elilawulwa yinkqubo, kunye nokusebenza kabini kuyo, ukunyusa kunye nokunciphisa. Ukuba iyasilela ukunciphisa, zero, ngoko umsonto wokufowuna uvaliwe. Xa inani linyuswa ngomnye umsonto osebenzayo / inkqubo, emva koko imisonto iyatsitywa kwaye i-semaphore iphinda icuthwe ngenani eligqithisiweyo. Omnye unokucinga ngoololiwe kwi-bottleneck ene-semaphore. .NET inikezela ngolwakhiwo oluninzi olunomsebenzi ofanayo: AutoResetEvent, ManualResetEvent, Mutex kunye nam Semaphore. Siza kusebenzisa AutoResetEvent, le yeyona nto ilula kolu lwakhiwo: amaxabiso amabini kuphela 0 kunye ne-1 (bubuxoki, yinyaniso). Indlela Yakhe WaitOne() ibhloka umsonto wokufowuna ukuba ixabiso belingu-0, kwaye ukuba u-1, yehlisela ku-0 kwaye iyitsibe. Indlela Set() inyusa ukuya ku-1 kwaye ivumela iweyitala enye iphumele, ethi iphinde yehlele ku-0. Isebenza njengendawo ejika kaloliwe engaphantsi komhlaba.

Masidibanise isisombululo kwaye sisebenzise isitshixo kwisithandi sobulumko ngasinye, kwaye hayi sonke ngaxeshanye. Ezo. ngoku kunokubakho iintanda-bulumko ezininzi ngexesha elinye, hayi enye. Kodwa siphinde sithintele ukufikelela kwitafile ukuze ngokuchanekileyo, siphephe iintlanga (iimeko zobuhlanga), thatha ii-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();
}

Ukuze uqonde okwenzekayo apha, qwalasela imeko xa isazi sefilosofi asiphumelelanga ukuthatha iifolokhwe, ngoko izenzo zakhe ziya kuba ngolu hlobo lulandelayo. Ulindele ukufikelela etafileni. Akuba eyifumene, uzama ukuthabatha iifolokhwe. Ayizange isebenze. Inika ukufikelela kwitheyibhile (ukukhutshelwa ecaleni). Kwaye udlula "indlela" yakhe (AutoResetEvent) (zivuliwe ekuqaleni). Ingena kumjikelo kwakhona, kuba akanazo iifolokhwe. Uzama ukuzithatha aze ame "kwi-turnstile" yakhe. Omnye ummelwane onethamsanqa ngasekunene okanye ngasekhohlo, ukugqiba ukutya, uvula ifilosofi yethu, "evula i-turntile yakhe." Ifilosofi yethu iyayidlula (kwaye ivala emva kwayo) okwesibini. Uzama okwesithathu ukuthatha iifolokhwe. Nqwenelela impumelelo. Kwaye udlulisa ithuba lakhe lokutya.

Xa kukho iimpazamo ezingalindelekanga kwikhowudi enjalo (zihlala zikhona), umzekelo, ummelwane uchazwe ngokungachanekanga okanye kwenziwa into efanayo. AutoResetEvent yabo bonke (Enumerable.Repeat), ngoko iifilosofi ziya kuba zilindele abaphuhlisi, kuba Ukufumana iimpazamo kwikhowudi enjalo ngumsebenzi onzima kakhulu. Enye ingxaki yesi sisombululo kukuba ayiqinisekisi ukuba enye ifilosofi ayiyi kulamba.

Izisombululo zeHybrid

Sijonge iindlela ezimbini zokubeka ixesha, xa sihlala kwimowudi yomsebenzisi kunye ne-loop, kwaye xa sivimba umsonto ngekernel. Indlela yokuqala ilungile kwizitshixo ezimfutshane, okwesibini ixesha elide. Ngokuqhelekileyo kuyimfuneko ukuba uqale ulinde ngokufutshane ukuba utshintsho lutshintshe kwi-loop, kwaye emva koko uvale intambo xa ukulinda kukude. Le ndlela iphunyezwa kwinto ebizwa. izakhiwo hybrid. Nazi izinto ezifanayo zokwakha njengemowudi ye-kernel, kodwa ngoku ngemowudi yomsebenzisi loop: SemaphorSlim, ManualResetEventSlim njl Uyilo oludumileyo apha lu Monitor, ngokuba kwi C # kukho eyaziwayo lock isintaksi. Monitor le semaphore efanayo kunye nexabiso eliphezulu le-1 (mutex), kodwa ngenkxaso yokulinda kwi-loop, i-recursion, i-Condition Variable pattern (ngaphezulu kwelo lingezantsi), njl. Makhe sijonge isisombululo ngayo.

// БпрячСм ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ для ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€Π° ΠΎΡ‚ всСх, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π±Π΅Π· Π΄Π΅Π΄Π»ΠΎΠΊΠΎΠ².
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);
    }
}

Apha siphinda sivala itafile yonke ukuba ifikelele kwiifolokhwe, kodwa ngoku sivula yonke imisonto ngaxeshanye, hayi abamelwane xa umntu egqiba ukutya. Ezo. kuqala, umntu udla kwaye avale abamelwane, kwaye xa lo mntu egqiba, kodwa ufuna ukutya kwakhona ngoko nangoko, ungena ebhlokweni kwaye uvusa abamelwane bakhe, kuba. ixesha layo lokulinda lincinci.

Le yindlela esiphepha ngayo i-deadlocks kunye nendlala yesithandi sobulumko. Sisebenzisa i-loop ukulinda okufutshane kwaye sivale intambo ixesha elide. Ukuvula wonke umntu ngaxeshanye kuyacotha kunokuba ummelwane ebengavaliwe, njengakwisisombululo AutoResetEvent, kodwa umahluko akufanele ube mkhulu, kuba imisonto kufuneka ihlale kwindlela yomsebenzisi kuqala.

Π£ lock I-syntax inezothuso ezimbi. Ncoma ukusebenzisa Monitor ngqo [Richter] [Eric Lippert]. Enye yazo yileyo lock rhoqo ngaphandle Monitor, nokuba bekukho ngaphandle, apho omnye umsonto unokutshintsha imeko yememori ekwabelwanayo. Kwiimeko ezinjalo, kudla ngokuba ngcono ukuya kwi-deadlock okanye ngandlela thile ukuphelisa inkqubo ngokukhuselekileyo. Enye into emangalisayo kukuba iMonitor isebenzisa iibhloko zongqamaniso (SyncBlock), ezikhoyo kuzo zonke izinto. Ngoko ke, ukuba kukhethwe into engafanelekanga, unokufumana lula ukuvala (umzekelo, ukuba utshixa kumtya ongaphakathi). Sisebenzisa into ehlala ifihliwe kule nto.

Ipatheni yoKuguquguquka kwemeko ikuvumela ukuba uphumeze ngokuthe kratya ulindelo lwemeko ethile entsonkothileyo. Kwi-NET, ayiphelelanga, ngokombono wam, kuba kwithiyori, kufuneka kubekho imigca emininzi kwizinto ezahlukeneyo (njengakwiPosix Threads), kwaye hayi kwi lok enye. Emva koko umntu unokuzenzela zonke iintanda-bulumko. Kodwa nakule fomu, ikuvumela ukuba unciphise ikhowudi.

ezininzi zobulumko okanye async / await

Kulungile, ngoku singakwazi ukubhloka imisonto ngokufanelekileyo. Kodwa kuthekani ukuba sineentanda-bulumko ezininzi? I-100? 10000? Umzekelo, sifumene izicelo eziyi-100000 kwiseva yewebhu. Kuya kuba ngaphezulu ukwenza intambo kwisicelo ngasinye, kuba imisonto emininzi kangaka ayizukuqhuba ngaxeshanye. Iza kubaleka kuphela kangangoko kukho iicores ezisengqiqweni (ndine-4). Kwaye bonke abanye baya kuthatha nje izixhobo. Esinye isisombululo kule ngxaki yi-async / ipateni yokulinda. Ingcamango yayo kukuba umsebenzi awuwubambi umsonto ukuba ufuna ukulinda into eqhubekayo. Kwaye xa isenza into, iphinda iqalise ukuphunyezwa kwayo (kodwa hayi kumsonto ofanayo!). Kwimeko yethu, siya kulinda ifolokhwe.

SemaphoreSlim inele nto WaitAsync() indlela. Nantsi ukuphunyezwa usebenzisa le pateni.

// Запуск Ρ‚Π°ΠΊΠΎΠΉ ΠΆΠ΅, ΠΊΠ°ΠΊ Ρ€Π°Π½ΡŒΡˆΠ΅. Π“Π΄Π΅-Π½ΠΈΠ±ΡƒΠ΄ΡŒ Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ΅:
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();
}

Indlela nge async / await iguqulelwe kumatshini welizwe olukhohlisayo olubuyisela ngokukhawuleza lwangaphakathi Task. Ngayo, unokulinda ukugqitywa kwendlela, uyirhoxise, nayo yonke enye into onokuyenza ngoMsebenzi. Ngaphakathi kwendlela, umatshini wombuso ulawula ukubulawa. Eyona nto iphambili kukuba ukuba akukho kulibaziseka, ngoko ukuphunyezwa ku-synchronous, kwaye ukuba kukho, ngoko intambo iyakhululwa. Ukuqonda ngcono oku, kungcono ukujonga lo matshini karhulumente. Unokwenza amatyathanga kwezi async / await iindlela.

Masivavanye. Umsebenzi weefilosofi ezili-100 kumatshini onama-4 cores anengqondo, imizuzwana eyi-8. Isisombululo sangaphambili kunye neMonitor siqhube kuphela imisonto emi-4 yokuqala kwaye yonke eminye ayizange ibaleke kwaphela. Ngamnye kule misonto mi-4 yayingasebenzi malunga ne-2ms. Kwaye isisombululo se-async / sokulinda siqhube zonke ii-100, kunye nokulinda okuphakathi kwe-6.8 imizuzwana nganye. Ewe, kwiinkqubo zokwenyani, ukungenzi nto kwimizuzwana emi-6 akwamkelekanga kwaye kungcono ukuba ungaqhubeki nezicelo ezininzi ezinje. Isisombululo esineMonitor siye sabonakala singenabungozi kwaphela.

isiphelo

Njengoko unokubona kule mizekelo mincinci, .NET ixhasa ungqamaniso olwakhayo oluninzi. Nangona kunjalo, akusoloko kubonakala indlela yokuzisebenzisa. Ndiyathemba ukuba eli nqaku libe luncedo. Okwangoku, oku kukuphela, kodwa kusekho izinto ezininzi ezinomdla ezisele, umzekelo, ukuqokelelwa kwentambo, i-TPL Dataflow, i-Reactive programming, imodeli ye-Software Transaction, njl.

Imithombo

umthombo: www.habr.com

Yongeza izimvo