Amafilosofi Aphakele Kahle noma Competitive .NET Programming

Amafilosofi Aphakele Kahle noma Competitive .NET Programming

Ake sibone ukuthi ukuhlela okuhambisanayo nokuhambisanayo kusebenza kanjani ku-.Net, kusetshenziswa i-Philosophers Dining Problem njengesibonelo. Uhlelo yilo, kusukela ekuvumelaneni kwemicu / izinqubo, kuya kumodeli yomlingisi (ezingxenyeni ezilandelayo). Lesi sihloko singase sibe usizo kumuntu wokuqala omaziyo noma ukuze sivuselele ulwazi lwakho.

Kungani ukwenza? Ama-Transistors afinyelela ubukhulu bawo obuncane, umthetho kaMoore uncike ekukhawulweni kwejubane lokukhanya ngakho-ke ukwanda kubonakala enanini, ama-transistors amaningi angenziwa. Ngesikhathi esifanayo, inani ledatha liyakhula, futhi abasebenzisi balindele impendulo esheshayo evela kumasistimu. Esimeni esinjalo, ukuhlela "okujwayelekile", lapho sinentambo eyodwa yokukhipha, ayisasebenzi. Udinga ngandlela thize ukuxazulula inkinga yokubulawa ngesikhathi esisodwa noma ngesikhathi esisodwa. Ngaphezu kwalokho, le nkinga ikhona emazingeni ahlukene: ezingeni lezintambo, ezingeni lezinqubo, ezingeni lemishini kunethiwekhi (izinhlelo ezisatshalaliswa). I-.NET inobuchwepheshe obusezingeni eliphezulu, obuhlolwe isikhathi bokuxazulula izinkinga ezinjalo ngokushesha nangempumelelo.

Inhloso

U-Edsger Dijkstra ubeke le nkinga kubafundi bakhe kusukela ngo-1965. Ukwakhiwa okusunguliwe kungokulandelayo. Kukhona inombolo ethile (imvamisa emihlanu) yezazi zefilosofi kanye nenani elifanayo lezimfoloko. Bahlala etafuleni eliyindilinga, kukhona izimfoloko phakathi kwabo. Izazi zefilosofi zingadla emapuletini azo okudla okungapheli, zicabange noma zilinde. Ukuze udle isazi sefilosofi, udinga ukuthatha izimfoloko ezimbili (owokugcina wabelana ngemfoloko neyokuqala). Ukucosha nokubeka phansi imfoloko kuyizenzo ezimbili ezihlukene. Zonke izazi zefilosofi zithule. Umsebenzi ukuthola i-algorithm enjalo ukuthi bonke bangacabanga futhi bagcwale ngisho nangemva kweminyaka engama-54.

Okokuqala, ake sizame ukuxazulula le nkinga ngokusebenzisa indawo okwabelwana ngayo. Izimfoloko zilele etafuleni elivamile futhi izazi zefilosofi zivele zizithathe lapho zikhona bese ziwabuyisela emuva. Lapha kunezinkinga ngokuvumelanisa, nini ngempela ukuthatha ama-surebets? kuthiwani uma ingekho imfoloko? njll. Kodwa okokuqala, ake siqale izazi zefilosofi.

Ukuqala imicu, sisebenzisa i-thread pool ngokusebenzisa 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);
}

I-thread pool yakhelwe ukuthuthukisa ukudalwa nokususwa kwentambo. Leli chibi linomugqa onemisebenzi futhi i-CLR idala noma isuse imicu kuye ngenani lale misebenzi. Iphuli eyodwa yawo wonke ama-AppDomains. Leli chibi kufanele lisetshenziswe cishe njalo, ngoba. asikho isidingo sokuzihlupha ngokudala, ukususa izintambo, imigqa yazo, njll. Kungenzeka ngaphandle kwechibi, kodwa-ke kufanele ulisebenzise ngokuqondile. Thread, lokhu kuyasiza ezimweni lapho udinga ukushintsha okubalulekile kochungechunge, lapho sinomsebenzi omude, wentambo yangaphambili, njll.

Ngamanye amazwi, System.Threading.Tasks.Task ikilasi liyafana Thread, kodwa ngazo zonke izinhlobo zokunethezeka: ikhono lokuqhuba umsebenzi ngemva kwebhlokhi yeminye imisebenzi, ukuyibuyisela emisebenzini, ukuyiphazamisa kalula, nokuningi. njll. Ziyadingeka ukuze zisekele ukwakhiwa kwe-async / ukulinda (Iphethini ye-Asynchronous esekelwe kumsebenzi, ushukela we-syntactic wokulinda ukusebenza kwe-IO). Sizokhuluma ngalokhu kamuva.

CancelationTokenSource lapha kuyadingeka ukuze intambo ikwazi ukuziqeda ngokwayo ngesignali yentambo yokubiza.

Vumelanisa Izinkinga

Izazi Zefilosofi Ezivinjiwe

Kulungile, siyayazi indlela yokwenza imicu, ake sizame ukudla kwasemini:

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

Lapha siqala sizama ukuthatha imfoloko yesokunxele, bese kuba imfoloko efanele, futhi uma isebenza, bese siyadla futhi siyibuyisele. Ukuthatha imfoloko eyodwa kuyi-athomu, i.e. imicu emibili ayikwazi ukuthatha eyodwa ngesikhathi esisodwa (engalungile: eyokuqala ifunda ukuthi imfoloko ikhululekile, eyesibili - futhi, eyokuqala ithatha, eyesibili ithatha). Kwalokhu Interlocked.CompareExchange, okufanele isetshenziswe ngomyalelo wokucubungula (TSL, XCHG), evala ingxenye yenkumbulo yokufunda nokubhala ngokulandelana kwe-athomu. Futhi i-SpinWait ilingana nokwakhiwa while(true) kuphela "ngomlingo" omncane - intambo ithatha iprosesa (Thread.SpinWait), kodwa ngezinye izikhathi idlulisela ukulawula kolunye uchungechunge (Thread.Yeild) noma alale (Thread.Sleep).

Kodwa lesi sixazululo asisebenzi, ngoba ukugeleza ngokushesha (kimi phakathi nomzuzwana) kuvinjiwe: zonke izazi zefilosofi zithatha imfoloko yazo yesokunxele, kodwa hhayi elungile. Amalungu afanayo amafoloko abe namanani: 1 2 3 4 5.

Amafilosofi Aphakele Kahle noma Competitive .NET Programming

Emfanekisweni, izintambo zokuvimbela (i-deadlock). Okuluhlaza - ukubulawa, okubomvu - ukuvumelanisa, okumpunga - intambo ilele. Ama-rhombuse akhombisa isikhathi sokuqala Semisebenzi.

Indlala Yezazi Zefilosofi

Nakuba akudingekile ukucabanga ikakhulukazi ukudla okuningi, kodwa indlala yenza noma ubani alahle ifilosofi. Ake sizame ukulingisa isimo sokulamba kwemicu enkingeni yethu. Indlala yilapho intambo isebenza, kodwa ngaphandle komsebenzi obalulekile, ngamanye amazwi, lokhu kufana ne-deadlock, kuphela manje intambo ayilali, kodwa ibheka ngenkuthalo into engayidla, kodwa akukho ukudla. Ukuze sigweme ukuvimba njalo, sizobuyisela imfoloko uma singakwazi 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)
    );
}

Okubalulekile ngale khodi ukuthi izazi zefilosofi ezimbili kwezine ziyakhohlwa ukubeka phansi imfoloko yazo yangakwesobunxele. Futhi kuvela ukuthi badla ukudla okuningi, kuyilapho abanye beqala ukulamba, nakuba imicu inokubaluleka okufanayo. Lapha abalambi ngokuphelele, ngoba. izazi zefilosofi ezimbi zibuyisela izimfoloko zazo emuva ngezinye izikhathi. Kuvela ukuthi abantu abalungile badla cishe izikhathi ezi-5 kunababi. Ngakho iphutha elincane kukhodi liholela ekwehleni kokusebenza. Kuyafaneleka futhi ukuqaphela lapha ukuthi isimo esingavamile singenzeka lapho zonke izazi zefilosofi zithatha imfoloko yesokunxele, akukho kwesokudla, babeka kwesokunxele, balinde, bathathe kwesokunxele futhi, njll. Lesi simo naso siyindlala, sifana ne-deadlock. Ngehlulekile ukuyiphinda. Ngezansi isithombe sesimo lapho izazi zefilosofi ezimbi zithathe zombili izimfoloko kanti ezimbili ezinhle zibulawa yindlala.

Amafilosofi Aphakele Kahle noma Competitive .NET Programming

Lapha ungabona ukuthi izintambo zivuka ngezinye izikhathi bese uzama ukuthola insiza. Ama-cores amabili kwamane awenzi lutho (igrafu eluhlaza ngenhla).

Ukufa Kwesazi Sefilosofi

Nokho, enye inkinga engaphazamisa isidlo sakusihlwa esikhazimulayo sezazi zefilosofi uma omunye wabo efa ngokuzumayo ephethe izimfoloko ezandleni zakhe (futhi bazomngcwaba kanjalo). Khona omakhelwane bazosala bengadlile. Ungakwazi ukuza nekhodi eyisibonelo yaleli cala ngokwakho, isibonelo, ilahliwe NullReferenceException ngemva kokuba isazi sefilosofi sithathe izimfoloko. Futhi, ngendlela, okuhlukile ngeke kuphathwe futhi ikhodi yocingo ngeke nje ibambe (ngenxa yalokhu AppDomain.CurrentDomain.UnhandledException futhi njll.). Ngakho-ke, izibambi zamaphutha ziyadingeka entanjeni ngokwazo futhi ngokunqanyulwa okuhle.

Weta

Kulungile, siyixazulula kanjani le nkinga yokufa, indlala, kanye nokufa? Sizovumela isazi sefilosofi esisodwa kuphela ukuthi sifinyelele izimfoloko, singeze ukukhishwa okufanayo kochungechunge lwale ndawo. Kwenziwa kanjani? Ake sithi kunoweta eduze kwezazi zefilosofi onikeza noma yisiphi isazi sefilosofi imvume yokuthatha izimfoloko. Senza kanjani lesi sikhonzi nokuthi izazi zefilosofi zizombuza kanjani, imibuzo iyathakazelisa.

Indlela elula yilapho izazi zefilosofi zizomane zibuze njalo uweta ukuze zifinyelele izimfoloko. Labo. manje amafilosofi ngeke alinde imfoloko eduze, kodwa alinde noma abuze uweta. Ekuqaleni, sisebenzisa kuphela Isikhala somsebenzisi salokhu, kuso asisebenzisi iziphazamiso ukubiza noma yiziphi izinqubo kusuka ku-kernel (mayelana nazo ngezansi).

Izixazululo endaweni yomsebenzisi

Lapha sizokwenza okufanayo njengoba sasivame ukwenza ngemfoloko eyodwa kanye nezazi zefilosofi ezimbili, sizojikeleza ngomjikelezo bese silinda. Kodwa manje kuzoba zonke izazi zefilosofi futhi, njengokungathi, imfoloko eyodwa kuphela, i.e. kungashiwo ukuthi yisazi sefilosofi kuphela esathatha le β€œmfoloko yegolide” kuweta ezodla. Kulokhu sisebenzisa i-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 lokhu kuyisivimbeli, ngokulingana, okufanayo while(true) { if (!lock) break; }, kodwa "ngomlingo" owengeziwe kunaku-in SpinWait (esetshenziswa lapho). Manje useyakwazi ukubala abalindile, abalalise kancane, nangaphezulu. njll. Ngokuvamile, yenza konke okusemandleni ukuthuthukisa. Kodwa kufanele sikhumbule ukuthi lokhu kusewumjikelezo ofanayo osebenzayo odla izinsiza zeprosesa futhi ugcine ukugeleza, okungaholela ekulambeni uma omunye wezazi zefilosofi eba obaluleke kakhulu kunabanye, kodwa engenayo imfoloko yegolide (Inkinga ye-Priority Inversion) . Ngakho-ke, siyisebenzisela izinguquko ezimfushane kakhulu kwinkumbulo eyabiwe kuphela, ngaphandle kwezingcingo zezinkampani zangaphandle, izingidi ezifakwe esidlekeni, nezinye izimanga.

Amafilosofi Aphakele Kahle noma Competitive .NET Programming

Umdwebo we SpinLock. Imifudlana "ilwela" njalo imfoloko yegolide. Kukhona ukwehluleka - kumfanekiso, indawo ekhethiwe. Ama-cores awasetshenziswa ngokugcwele: cishe 2/3 kuphela ngale micu emine.

Esinye isixazululo lapha kungaba ukusebenzisa kuphela Interlocked.CompareExchange ngokulinda okufanayo okusebenzayo njengoba kukhonjisiwe kukhodi engenhla (kuzazi zefilosofi ezilambile), kodwa lokhu, njengoba sekushiwo, kungase kuholele ekuvimbeni.

Mayelana Interlocked Kufanele kuqashelwe ukuthi akukhona kuphela CompareExchange, kodwa nezinye izindlela zokufunda NOKUBHALA nge-athomu. Futhi ngokuphindaphindiwe koshintsho, uma kwenzeka enye intambo inesikhathi sokwenza izinguquko zayo (funda 1, funda 2, bhala 2, bhala 1 yimbi), ingasetshenziselwa izinguquko eziyinkimbinkimbi kwinani elilodwa (I-Interlocked Anything iphethini) .

Izixazululo Zemodi Ye-Kernel

Ukugwema ukumosha izinsiza ku-loop, ake sibone ukuthi singawuvimba kanjani uchungechunge. Ngamanye amazwi, siqhubeka nesibonelo sethu, ake sibone ukuthi uweta usilalisa kanjani isazi sefilosofi futhi asivuse kuphela lapho kudingekile. Okokuqala, ake sibheke ukuthi singakwenza kanjani lokhu ngemodi ye-kernel yesistimu yokusebenza. Zonke izakhiwo lapho zivame ukuhamba kancane kunalezo ezisesikhaleni somsebenzisi. Izikhathi ezimbalwa ezihamba kancane, isibonelo AutoResetEvent mhlawumbe 53 izikhathi kancane SpinLock [Richter]. Kodwa ngosizo lwabo, ungakwazi ukuvumelanisa izinqubo kulo lonke uhlelo, oluphethwe noma cha.

Ukwakhiwa okuyisisekelo lapha i-semaphore ephakanyiswe yi-Dijkstra eminyakeni engaphezu kwekhulu leminyaka edlule. I-semaphore, kalula nje, iyinani eliphelele eliphethwe uhlelo, kanye nokusebenza okubili kulo, ukukhuphuka nokuncipha. Uma ihluleka ukuncipha, uziro, khona-ke uchungechunge lokushaya luyavinjwa. Uma inombolo inyuswa ngenye intambo/inqubo esebenzayo, imicu iyayeqiwa futhi i-semephore iphinde yehliswe ngenombolo edlulisiwe. Umuntu angacabanga izitimela ebhodleleni eline-semaphore. I-.NET inikeza izakhiwo ezimbalwa ezinomsebenzi ofanayo: AutoResetEvent, ManualResetEvent, Mutex nami uqobo Semaphore. Sizosebenzisa AutoResetEvent, lokhu kulula kakhulu kulezi zakhiwo: amanani amabili kuphela 0 no-1 (amanga, iqiniso). Indlela Yakhe WaitOne() ivimba intambo yokubiza uma inani lalingu-0, futhi uma u-1, liyehlisela ku-0 futhi liyeqe. Indlela Set() ikhuphukela ku-1 futhi idedela uweta oyedwa, ophinde wehlele ku-0. Isebenza njengesitimela esingaphansi komhlaba.

Masixabanise isixazululo futhi sisebenzise isikhiya sefilosofi ngayinye, hhayi sonke ngesikhathi esisodwa. Labo. manje kungaba khona izazi zefilosofi eziningana ngesikhathi esisodwa, hhayi eyodwa. Kodwa siphinde sivimbe ukufinyelela etafuleni ukuze senze kahle, sigweme izinhlanga (izimo zomjaho), sithathe ama-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 ukuthi kwenzekani lapha, cabangela icala lapho isazi sefilosofi sihluleka ukuthatha izimfoloko, khona-ke izenzo zakhe zizoba kanje. Ulindele ukungena etafuleni. Eseyitholile, azame ukuthatha izimfoloko. Akusebenzanga. Inikeza ukufinyelela kuthebula (ukukhishwa okufanayo). Futhi udlula "ijika" lakhe (AutoResetEvent) (ziqale zivuliwe). Ingena emjikelezweni futhi, ngoba akanazo izimfoloko. Azame ukuwathatha ame "ejikajika" lakhe. Omunye umakhelwane onenhlanhla kwesokudla noma kwesobunxele, eseqedile ukudla, uvula isazi sefilosofi yethu, "evula i-turnstile yakhe." Isazi sefilosofi yethu siyayidlulisa (futhi ivale ngemuva kwayo) okwesibili. Azame okwesithathu ukuthatha izimfoloko. Ngikufisela inhlanhla. Futhi udlulisa ijika lakhe ukuze adle.

Uma kukhona amaphutha angahleliwe kukhodi enjalo (ahlala ekhona), isibonelo, umakhelwane uchazwe ngokungalungile noma kwakhiwa into efanayo. AutoResetEvent kwabo bonke (Enumerable.Repeat), khona-ke izazi zefilosofi zizolinda abathuthukisi, ngoba Ukuthola amaphutha kukhodi enjalo kuwumsebenzi onzima. Enye inkinga ngalesi sixazululo ukuthi akuqinisekisi ukuthi esinye isazi sefilosofi ngeke silambe.

Izixazululo ze-Hybrid

Sibheke izindlela ezimbili zokubala isikhathi, lapho sihlala kumodi yomsebenzisi ne-loop, nalapho sivimba uchungechunge ngekernel. Indlela yokuqala ilungele ukukhiya okufushane, okwesibili kumade. Ngokuvamile kuyadingeka ukuthi uqale ulinde kafushane ukushintshashintsha ku-loop, bese uvimba intambo lapho ukulinda kukude. Le ndlela isetshenziswa kulokho okubizwa ngokuthi. izakhiwo ezixubile. Nazi izinto ezifanayo zokwakha njengemodi ye-kernel, kodwa manje nge-loop yemodi yomsebenzisi: SemaphorSlim, ManualResetEventSlim njll. Umklamo odume kakhulu lapha Monitor, ngoba ku C# kukhona owaziwayo lock i-syntax. Monitor lena i-semaphore efanayo enenani eliphakeme elingu-1 (mutex), kodwa ngokusekelwa kokulinda ku-loop, i-recursion, iphethini ye-Condition Variable (ngaphezulu kulokho ngezansi), njll. Ake sibheke isisombululo ngakho.

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

Lapha siphinda sivimba itafula lonke ukuze sifinyelele izimfoloko, kodwa manje sivula yonke imicu ngesikhathi esisodwa, hhayi omakhelwane uma umuntu eqeda ukudla. Labo. kuqale umuntu adle avimbe omakhelwane kuthi uma eqeda lokhu kodwa aphinde adle khona manjalo angene avimbe avuse omakhelwane ngoba. isikhathi sayo sokulinda sincane.

Yile ndlela esigwema ngayo imigoqo kanye nendlala yesazi esithile sefilosofi. Sisebenzisa i-loop ukulinda okufushane futhi sivimba intambo isikhathi eside. Ukuvulela wonke umuntu ngesikhathi esisodwa kuhamba kancane kunalokho uma umakhelwane evuliwe, njengasesixazululweni ngo AutoResetEvent, kodwa umehluko akufanele ube mkhulu, ngoba imicu kufanele ihlale kumodi yomsebenzisi kuqala.

Π£ lock I-syntax inezimanga ezimbi. Ncoma ukusebenzisa Monitor ngqo [Richter] [Eric Lippert]. Enye yazo yilokho lock njalo ngaphandle Monitor, noma ngabe bekukhona okuhlukile, lapho enye intambo ingashintsha isimo sememori eyabiwe. Ezimweni ezinjalo, kuvame ukuba ngcono ukuya ku-deadlock noma ukunqamula uhlelo ngokuphepha. Okunye okumangazayo ukuthi i-Monitor isebenzisa amabhulokhi okuvumelanisa (SyncBlock), ezikhona kuzo zonke izinto. Ngakho-ke, uma kukhethwa into engafanele, ungathola kalula i-deadlock (isibonelo, uma ukhiyela ochungechungeni oluhlanganisiwe). Sisebenzisa into ehlale ifihliwe kulokhu.

Iphethini ye-Condition Variable ikuvumela ukuthi usebenzise kafushane okulindelekile kwesimo esithile esiyinkimbinkimbi. Ku-NET, ayiphelele, ngombono wami, ngoba ngokombono, kufanele kube nemigqa eminingana kokuguquguqukayo okuningana (njengaku-Posix Threads), hhayi ku-lok eyodwa. Khona-ke umuntu angabenzela zonke izazi zefilosofi. Kodwa nakuleli fomu, likuvumela ukuthi unciphise ikhodi.

izazi zefilosofi eziningi noma async / await

Kulungile, manje singavimba uchungechunge ngempumelelo. Kodwa kuthiwani uma sinezazi zefilosofi eziningi? 100? 10000? Isibonelo, sithole izicelo eziyi-100000 kuseva yewebhu. Kuzoba phezulu ukudala intambo yesicelo ngasinye, ngoba imicu eminingi kangaka ngeke isebenze ngokufana. Izogijima kuphela njengoba kunama-core cores (ngina-4). Futhi bonke abanye bazovele bathathe izinsiza. Isixazululo esisodwa sale nkinga iphethini ye-async / yokulinda. Umbono wayo ukuthi umsebenzi awubambi intambo uma idinga ukulinda okuthile ukuze kuqhubeke. Futhi lapho yenza okuthile, iqala kabusha ukubulawa kwayo (kodwa hhayi emculweni ofanayo!). Esimeni sethu, sizolinda imfoloko.

SemaphoreSlim inalokhu WaitAsync() indlela. Nakhu ukuqaliswa kusetshenziswa le phethini.

// Запуск Ρ‚Π°ΠΊΠΎΠΉ ΠΆΠ΅, ΠΊΠ°ΠΊ Ρ€Π°Π½ΡŒΡˆΠ΅. Π“Π΄Π΅-Π½ΠΈΠ±ΡƒΠ΄ΡŒ Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ΅:
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 ihunyushwa emshinini wesimo okhohlisayo obuyisela ngokushesha okungaphakathi kwawo Task. Ngayo, ungalinda ukuqedwa kwendlela, uyikhansele, nakho konke okunye ongakwenza nge-Task. Ngaphakathi kwendlela, umshini wombuso ulawula ukubulawa. Okubalulekile ukuthi uma kungekho ukubambezeleka, khona-ke ukubulawa kuyavumelana, futhi uma kukhona, khona-ke intambo iyakhululwa. Ukuze uqonde kangcono lokhu, kungcono ukubheka lo mshini wombuso. Ungakha amaketanga kusuka kulawa async / await izindlela.

Ake sihlole. Umsebenzi wezazi zefilosofi eziyi-100 emshinini onamaphuzu angu-4 anengqondo, imizuzwana engu-8. Isixazululo sangaphambilini esine-Monitor sisebenzise imicu yokuqala emi-4 kuphela futhi eminye ayizange isebenze nhlobo. Ngayinye yale micu emi-4 ibingasebenzi isikhathi esingaba ngu-2ms. Futhi isixazululo se-async / sokulinda sisebenze wonke angu-100, ngokulinda okuphakathi kwamasekhondi angu-6.8 lilinye. Vele, ezinhlelweni zangempela, ukungenzi lutho imizuzwana eyi-6 akwamukelekile futhi kungcono ukungacubunguli izicelo eziningi ezinjengalezi. Isixazululo nge-Monitor sibonakale singenamandla neze.

isiphetho

Njengoba ubona kulezi zibonelo ezincane, i-.NET isekela ukwakha okuningi kokuvumelanisa. Nokho, akubonakali ngaso sonke isikhathi ukuthi zisetshenziswa kanjani. Ngithemba ukuthi lesi sihloko sibe usizo. Okwamanje, lokhu ukuphela, kodwa kusekhona izinto eziningi ezithakazelisayo ezisele, isibonelo, amaqoqo aphephile ngentambo, i-TPL Dataflow, i-Reactive programming, imodeli ye-Software Transaction, njll.

Imithombo

Source: www.habr.com

Engeza amazwana