Afilosofi Odyetsedwa Bwino kapena Competitive .NET Programming

Afilosofi Odyetsedwa Bwino kapena Competitive .NET Programming

Tiyeni tiwone momwe mapulogalamu amagwiritsidwira ntchito nthawi imodzi ndi ofanana mu .Net, pogwiritsa ntchito chitsanzo chavuto la nkhomaliro. Dongosololi lili motere, kuchokera ku ulusi / njira yolumikizirana kupita ku mtundu wa zisudzo (m'magawo otsatirawa). Nkhaniyo ingakhale yothandiza kwa munthu amene munam’dziΕ΅a poyamba kapena kukukumbutsaninso zimene mwadziΕ΅a.

Chifukwa chiyani mukudziwa momwe mungachitire izi? Ma Transistors akufika kukula kwawo kochepa, lamulo la Moore likugunda malire a liwiro la kuwala, motero kukula kumawonedwa mu manambala; ma transistors ambiri amatha kupangidwa. Panthawi imodzimodziyo, kuchuluka kwa deta kukukulirakulira, ndipo ogwiritsa ntchito amayembekezera kuyankha mwamsanga kuchokera ku machitidwe. Zikatero, mapulogalamu "abwinobwino", tikakhala ndi ulusi umodzi wokha, sagwiranso ntchito. Tiyenera mwanjira ina kuthetsa vuto la kupha nthawi imodzi kapena nthawi imodzi. Komanso, vutoli liripo pazigawo zosiyanasiyana: pa mlingo wa ulusi, pa mlingo wa ndondomeko, pa mlingo wa makina pa intaneti (machitidwe ogawidwa). .NET ili ndi matekinoloje apamwamba, oyesedwa nthawi kuti athetse mavutowa mwachangu komanso moyenera.

Cholinga

Edsger Dijkstra adafunsa vutoli kwa ophunzira ake kumbuyo mu 1965. Kukonzekera kokhazikitsidwa kuli motere. Pali ena (kawirikawiri asanu) chiwerengero cha afilosofi ndi chiwerengero chomwecho cha mafoloko. Amakhala patebulo lozungulira, mafoloko pakati pawo. Afilosofi amatha kudya kuchokera m'mbale zawo za chakudya chosatha, kuganiza kapena kuyembekezera. Kuti adye, wafilosofi ayenera kutenga mafoloko awiri (wachiwiri amagawana mphanda ndi woyamba). Kutola ndi kuyika mphanda ndi ntchito ziwiri zosiyana. Afilosofi onse ali chete. Ntchito ndikupeza ma aligorivimu kotero kuti onse kuganiza ndi kudyetsedwa bwino ngakhale patapita zaka 54.

Choyamba, tiyeni tiyesetse kuthetsa vutoli pogwiritsa ntchito malo ogawana. Mafoloko amagona patebulo wamba ndipo afilosofi amangowatenga pamene iwo alipo ndi kuwabwezeretsa. Apa ndi pamene mavuto ndi kalunzanitsidwe amayamba, pamene ndendende kutenga mafoloko? chochita ngati palibe pulagi? etc. Koma choyamba, tiyeni tiyambe ndi anthanthi.

Kuti tiyambe ulusi timagwiritsa ntchito dziwe la ulusi kudzera Task.Run njira:

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

Phukusi la ulusi lapangidwa kuti lizitha kupanga komanso kuchotsa ulusi. Phukusili lili ndi mndandanda wa ntchito ndipo CLR imapanga kapena kuchotsa ulusi malinga ndi kuchuluka kwa ntchitozi. Dziwe limodzi la AppDomains onse. Dziweli liyenera kugwiritsidwa ntchito pafupifupi nthawi zonse, chifukwa ... palibe chifukwa chodandaula ndi kupanga ndi kuchotsa ulusi, mizere yawo, etc. Mukhoza kuchita popanda dziwe, koma muyenera kuligwiritsa ntchito mwachindunji. Thread, izi ndizothandiza pamilandu pamene tikufunika kusintha ulusi woyambirira, tikakhala ndi ntchito yayitali, pa ulusi wa Foreground, etc.

Mwanjira ina, System.Threading.Tasks.Task class ndi yomweyo Thread, koma ndi mitundu yonse ya zabwino: kuthekera koyambitsa ntchito pambuyo pa chipika cha ntchito zina, kuwabweza ku ntchito, kuwasokoneza mosavuta, ndi zina zambiri. etc. Iwo amafunikira kuthandiza async/kuyembekezera zomanga (Task-based Asynchronous Pattern, syntactic sugar for wait for IO operations). Tikambirana pambuyo pake.

CancelationTokenSource apa pakufunika kuti ulusiwo udzilekeze pa chizindikiro chochokera ku ulusi woyitana.

Kulunzanitsa Nkhani

Afilosofi oletsedwa

Chabwino, tikudziwa kupanga ulusi, tiyeni tiyese nkhomaliro:

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

Pano timayesa kutenga kumanzere ndiyeno foloko yakumanja, ndipo ngati ikugwira ntchito, timadya ndikubwezeretsanso. Kutenga foloko imodzi ndi atomiki, i.e. ulusi uwiri sungathe kutenga imodzi nthawi imodzi (yolakwika: yoyamba imawerenga kuti mphanda ndi yaulere, yachiwiri imachita chimodzimodzi, yoyamba imatenga, yachiwiri imatenga). Za ichi Interlocked.CompareExchange, zomwe ziyenera kukhazikitsidwa pogwiritsa ntchito malangizo a processor (TSL, XCHG), chomwe chimatseka chikumbukiro kuti chiwerenge ndi kulemba motsatizana ndi atomiki. Ndipo SpinWait ndiyofanana ndi zomangamanga while(true) kokha ndi "matsenga" pang'ono - ulusi umatenga purosesa (Thread.SpinWait), koma nthawi zina amadutsa ulusi wina (Thread.Yeild) kapena kugona (Thread.Sleep).

Koma yankho ili silikugwira ntchito, chifukwa ... ulusi posachedwapa (mkati mwa sekondi kwa ine) watsekedwa: afilosofi onse amatenga mphanda wawo wakumanzere, koma palibe wolondola. Gulu la mafoloko ndiye lili ndi mfundo zake: 1 2 3 4 5.

Afilosofi Odyetsedwa Bwino kapena Competitive .NET Programming

Mu chithunzi, kutsekereza ulusi (deadlock). Chobiriwira chimasonyeza kuphedwa, chofiira chimasonyeza kugwirizanitsa, ndipo imvi imasonyeza kuti ulusi ukugona. Ma diamondi amawonetsa nthawi yoyambitsa ntchito.

Njala ya Afilosofi

Ngakhale kuti simukusowa chakudya chochuluka kuti muganizire, njala ikhoza kukakamiza aliyense kusiya filosofi. Tiyeni tiyesetse kutsanzira mkhalidwe wa njala ya ulusi mu vuto lathu. Njala ndi pamene ulusi ukugwira ntchito, koma popanda ntchito yaikulu, mwa kuyankhula kwina, ndizofanana ndi zomwezo, tsopano ulusi sukugona, koma ukuyang'ana mwachangu chakudya, koma palibe chakudya. Pofuna kupewa kutsekereza pafupipafupi, tidzabwezeretsanso foloko ngati sitingathe kutenga ina.

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

Chofunika kwambiri pa kachidindo kameneka ndi chakuti awiri mwa akatswiri anayi afilosofi amaiwala kuyika foloko yawo yakumanzere. Ndipo zimachitika kuti amadya chakudya chochuluka, ndipo ena amayamba kufa ndi njala, ngakhale kuti ulusiwo ndi wofanana. Kuno sakufa ndi njala, chifukwa... Afilosofi oipa nthawi zina amaika mafoloko awo kumbuyo. Zikuoneka kuti zabwino zimadya pafupifupi kasanu kuposa zoipa. Kotero cholakwika chaching'ono mu code chimayambitsa kutsika kwa ntchito. Apa ndiyeneranso kuzindikira kuti zochitika zosowa zimatheka pamene afilosofi onse atenga mphanda wakumanzere, palibe chabwino, amaika kumanzere pansi, kuyembekezera, kutenganso kumanzere, ndi zina zotero. Mkhalidwewu ulinso njala, ngati kutsekereza. Sindinathe kubwereza. Pansipa pali chithunzi cha zochitika pamene afilosofi awiri oipa atenga mafoloko onse awiri, ndipo awiri abwino akuvutika ndi njala.

Afilosofi Odyetsedwa Bwino kapena Competitive .NET Programming

Apa mutha kuwona kuti ulusi nthawi zina umadzuka ndikuyesa kupeza gwero. Ma cores awiri mwa anayi sachita chilichonse (chithunzi chobiriwira pamwambapa).

Imfa ya Wafilosofi

Chabwino, vuto lina lomwe lingathe kusokoneza chakudya chamadzulo chaulemerero cha afilosofi ndi ngati mmodzi wa iwo amwalira mwadzidzidzi ndi mafoloko m'manja mwake (ndipo adzaikidwa m'manda). Kenako oyandikana nawo adzatsala opanda chakudya chamasana. Mutha kubwera ndi code yachitsanzo ya nkhaniyi nokha, mwachitsanzo imatayidwa NullReferenceException pambuyo wafilosofi kutenga mafoloko. Ndipo, mwa njira, kuchotserako sikudzayendetsedwa ndipo nambala yoyimbira siingoyigwira (pa izi AppDomain.CurrentDomain.UnhandledException ndi etc.). Chifukwa chake, zowongolera zolakwika ndizofunikira mu ulusi womwewo komanso kutha kwabwino.

Wowonjezera

Chabwino, kodi timathetsa bwanji vuto ili lakufa, njala, ndi imfa? Tilola wanthanthi m'modzi yekha pa mafoloko, ndipo tiwonjezera kusagwirizana kwa ulusi pamalo ano. Kodi kuchita izo? Tiyerekeze kuti pafupi ndi anthanthi pali woperekera zakudya yemwe amapereka chilolezo kwa wafilosofi mmodzi kuti atenge mafoloko. Momwe tingapangire woperekera zakudya uyu komanso momwe afilosofi angamufunse ndi mafunso osangalatsa.

Njira yosavuta ndi yakuti afilosofi amangopempha woperekera zakudya nthawi zonse kuti apeze mafoloko. Iwo. Tsopano afilosofi sadzayembekezera mphanda pafupi, koma dikirani kapena funsani woperekera zakudya. Poyamba timagwiritsa ntchito Malo Ogwiritsa Ntchito Pa izi; momwemo sitigwiritsa ntchito zosokoneza kuyitanira njira zilizonse kuchokera ku kernel (zambiri pamunsimu).

Njira zothetsera danga

Apa tidzachita zomwezo zomwe tidachita kale ndi foloko imodzi ndi afilosofi awiri, tidzazungulira ndikudikirira. Koma tsopano adzakhala onse afilosofi ndipo, titero, mphanda umodzi wokha, i.e. tinganene kuti wafilosofi amene anatenga β€œfoloko lagolide” limeneli kwa woperekera zakudya ndi amene adzadye. Kuti tichite izi, timagwiritsa ntchito 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 ichi ndi blocker, ndi, pafupifupi, chimodzimodzi while(true) { if (!lock) break; }, koma ndi β€œmatsenga” ochuluka kuposa mmenemo SpinWait (zomwe zimagwiritsidwa ntchito pamenepo). Tsopano akudziwa kuwerengera amene akudikira, kuwagoneka pang'ono, ndi zina zambiri. etc. Mwambiri, imachita zonse zotheka kukhathamiritsa. Koma tiyenera kukumbukira kuti ichi akadali lopo yogwira yemweyo amadya purosesa chuma ndi kugwira ulusi, zomwe zingachititse njala ngati mmodzi wa filosofi kukhala patsogolo kuposa ena, koma alibe mphanda golide (Chofunika Inversion vuto. ). Chifukwa chake, timangogwiritsa ntchito pazosintha zazifupi kwambiri pamakumbukidwe omwe adagawana, popanda mafoni amtundu wina, maloko okhala, kapena zodabwitsa zina.

Afilosofi Odyetsedwa Bwino kapena Competitive .NET Programming

Kujambula kwa SpinLock. Mitsinje nthawi zonse "ikumenyera" mphanda wagolide. Zolephera zimachitika - gawo lomwe lawonetsedwa pachithunzichi. Ma cores sagwiritsidwa ntchito mokwanira: pafupifupi 2/3 ndi ulusi anayi awa.

Njira ina apa ingakhale yongogwiritsa ntchito Interlocked.CompareExchange ndikudikirira komweko monga momwe tawonetsera m'chithunzi pamwambapa (mwa anthanthi omwe ali ndi njala), koma izi, monga tanenera kale, zitha kuyambitsa kutsekereza.

pa Interlocked m'pofunika kunena kuti palibe CompareExchange, komanso njira zina zowerengera ndi kulemba atomiki. Ndipo pobwereza kusintha, ngati ulusi wina ungathe kusintha (werengani 1, werengani 2, lembani 2, lembani 1 ndi yoipa), ikhoza kugwiritsidwa ntchito pakusintha kovutirapo pamtengo umodzi (Yotsekedwa Chilichonse chitsanzo).

Mayankho a Kernel Mode

Kuti tipewe kuwononga zinthu mu lupu, tiyeni tiwone momwe tingatsekere ulusi. M’mawu ena, kupitiriza chitsanzo chathu, tiyeni tione mmene woperekera zakudya amagoneka wanthanthi ndi kumudzutsa pamene kuli kofunikira. Choyamba, tiyeni tiwone momwe tingachitire izi kudzera munjira ya kernel ya opareshoni. Zomangamanga zonse kumeneko nthawi zambiri zimakhala zochedwa kuposa zomwe zili m'malo ogwiritsa ntchito. Pang'onopang'ono kangapo, mwachitsanzo AutoResetEvent mwina 53 nthawi pang'onopang'ono SpinLock [Richter]. Koma ndi chithandizo chawo, mutha kulunzanitsa njira padongosolo lonse, loyendetsedwa kapena ayi.

Mapangidwe oyambira apa ndi semaphore, yopangidwa ndi Dijkstra zaka zoposa theka zapitazo. Semaphore ndi, mwachidule, chiwerengero chabwino cholamulidwa ndi dongosolo, ndi ntchito ziwiri pa izo - kuwonjezeka ndi kuchepa. Ngati sizingatheke kuchepetsa ziro, ndiye kuti ulusi woyitana watsekedwa. Chiwerengerocho chikachulukitsidwa ndi ulusi wina wogwira ntchito / ndondomeko, ndiye kuti ulusi umadutsa ndipo semaphore imachepetsedwanso ndi nambala yomwe yadutsa. Mutha kulingalira za masitima apabotolo okhala ndi semaphore. NET imapereka zomanga zingapo zokhala ndi magwiridwe antchito ofanana: AutoResetEvent, ManualResetEvent, Mutex ndi ine ndekha Semaphore. Tidzagwiritsa ntchito AutoResetEvent, ichi ndiye chosavuta kwambiri mwazomanga izi: zikhalidwe ziwiri zokha 0 ndi 1 (zabodza, zowona). Njira yake WaitOne() imatchinga ulusi woyimbira ngati mtengo wake unali 0, ndipo ngati 1, ndiye amatsitsa mpaka 0 ndikudumpha. Njira Set() imawonjezeka kufika pa 1 ndikulowetsa munthu m'modzi, yemwe amachepetsanso kufika pa 0. Amagwira ntchito ngati njira yapansi panthaka.

Tiyeni tiwunikire yankho ndikugwiritsa ntchito kutsekereza kwa wafilosofi aliyense, osati zonse nthawi imodzi. Iwo. Tsopano anthanthi angapo amatha kudya nthawi imodzi, osati imodzi yokha. Koma timaletsanso mwayi wopezeka patebulo kuti titenge bwino mafoloko, kupewa mikhalidwe yamtundu.

// Для блокирования ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ философа.
// Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅Ρ‚ΡΡ: 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();
}

Kuti mumvetse zomwe zikuchitika pano, taganizirani za pamene wafilosofiyo analephera kutenga mafoloko, ndiye zochita zake zidzakhala motere. Akudikirira mwayi wofika patebulo. Atalandira, amayesa kutenga mafoloko. Sizinatheke. Zimapereka mwayi wopezeka patebulo (kupatula onse). Ndipo amadutsa "njira" yake (AutoResetEvent) (poyamba amatsegula). Imagweranso mumkombero kachiwiri, chifukwa alibe mafoloko. Amayesa kuwatenga ndikuyima "panjira" yake. Ena oyandikana nawo mwayi kumanja kapena kumanzere, atamaliza kudya, amatsegula wanzeru wathu mwa "kutsegula njira yake." Wafilosofi wathu amadutsamo (ndipo amatseka kumbuyo kwake) kachiwiri. Yesani kachitatu kutenga mafoloko. Zapambana. Ndipo amadutsa panjira yake kuti akadye chakudya chamasana.

Pakakhala zolakwika mwachisawawa muzolemba zotere (zimakhalapo nthawi zonse), mwachitsanzo, woyandikana naye adzafotokozedwa molakwika kapena chinthu chomwecho chidzapangidwa. AutoResetEvent kwa onse (Enumerable.Repeat), ndiye afilosofi adzadikirira opanga, chifukwa Kupeza zolakwika mu code yotere ndi ntchito yovuta. Vuto lina ndi yankho limeneli nlakuti silikutsimikizira kuti wafilosofi wina sadzafa ndi njala.

Mayankho osakanizidwa

Tidayang'ana njira ziwiri zolumikizirana, tikakhala m'mawonekedwe a ogwiritsa ntchito ndikuzungulira kuzungulira komanso tikatsekereza ulusi kudzera mu kernel. Njira yoyamba ndi yabwino kwa midadada yayifupi, yachiwiri kwa yayitali. Nthawi zambiri muyenera kudikirira pang'ono kuti kusintha kusinthe mu lupu, ndiyeno kutsekereza ulusi pamene kudikira kwatalika. Njirayi ikugwiritsidwa ntchito mu otchedwa. zojambula zosakanizidwa. Ili ndi mapangidwe ofanana ndi a kernel mode, koma tsopano ndi loop-mode loop: SemaphorSlim, ManualResetEventSlim etc. Mapangidwe otchuka kwambiri apa ndi Monitor, chifukwa mu C # pali odziwika bwino lock mawu ofotokozera. Monitor iyi ndi semaphore yomweyi yokhala ndi mtengo wapamwamba wa 1 (mutex), koma ndi chithandizo chodikirira mu kuzungulira, kubwereranso, Condition Variable pattern (zambiri pamunsimu), ndi zina zotero. Tiyeni tiwone yankho ndi izo.

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

Apa timatsekanso tebulo lonse kuti lisalowe m'mafoloko, koma tsopano timatsegula ulusi wonse nthawi imodzi, osati oyandikana nawo pamene wina amaliza kudya. Iwo. Choyamba, munthu amadya ndikutsekereza anansi, ndipo munthu akamaliza, koma akufuna kudya nthawi yomweyo, amalowa mu block ndikudzutsa anansi ake, chifukwa. nthawi yake yodikira ndi yochepa.

Mwanjira imeneyi timapeΕ΅a zinthu zopanda pake ndi njala ya wafilosofi wina. Timagwiritsa ntchito lupu kuti tidikire kwakanthawi kochepa ndikutsekereza ulusi kwa nthawi yayitali. Kutsegula aliyense nthawi imodzi kumachedwa kuposa ngati woyandikana nawo adatsegulidwa, monga momwe adayankhira AutoResetEvent, koma kusiyana sikuyenera kukhala kwakukulu, chifukwa ulusi uyenera kukhala wogwiritsa ntchito poyamba.

Π£ lock syntax ili ndi zodabwitsa zina zosasangalatsa. Analimbikitsa ntchito Monitor mwachindunji [Richter] [Eric Lippert]. Chimodzi mwa izo ndi chimenecho lock nthawi zonse amatuluka Monitor, ngakhale kuti panali zosiyana, ndiyeno ulusi wina ukhoza kusintha mkhalidwe wa kukumbukira nawo. Zikatero, nthawi zambiri zimakhala bwino kuti mulowetse pulogalamuyo kapena kuyimitsa pulogalamuyo mosamala. Chodabwitsa china ndikuti Monitor amagwiritsa ntchito mawotchi (SyncBlock), zomwe zilipo muzinthu zonse. Chifukwa chake, ngati chinthu chosayenera chasankhidwa, mutha kupeza zotsekera (mwachitsanzo, ngati mutsekera chingwe cholumikizira). Nthawi zonse timagwiritsa ntchito chinthu chobisika pa izi.

Mtundu wa Condition Variable umakupatsani mwayi kuti mukwaniritse bwino zomwe mukuyembekezera zazovuta zina. Mu NET sizokwanira, m'malingaliro mwanga, chifukwa... Mwachidziwitso, payenera kukhala mizere ingapo pamitundu ingapo (monga mu Posix Threads), osati pa loko imodzi. Ndiye zikanakhala zotheka kuwapanga iwo afilosofi onse. Koma ngakhale mu mawonekedwe awa amakulolani kufupikitsa code.

Afilosofi ambiri kapena async / await

Chabwino, tsopano titha kuletsa ulusi bwino. Koma bwanji ngati tili ndi anthanthi ambiri? 100? 10000? Mwachitsanzo, talandira zopempha 100000 ku seva yapaintaneti. Kupanga ulusi pa pempho lililonse kudzakhala okwera mtengo, chifukwa kotero ulusi wambiri sudzagwiritsidwa ntchito mofanana. Ndi ma cores ambiri omveka omwe aperekedwa (ndili ndi 4). Ndipo wina aliyense amangotenga chuma. Njira imodzi yothetsera vutoli ndi async / wait pattern. Lingaliro lake ndikuti ntchito sikhala ndi ulusi ngati ikufunika kudikirira kuti chinachake chipitirire. Ndipo chinthu chikachitika, chimayambiranso kuphedwa kwake (koma osati mu ulusi womwewo!). Kwa ife, tidzadikira mphanda.

SemaphoreSlim za izi WaitAsync() njira. Pano pali kukhazikitsa pogwiritsa ntchito chitsanzo ichi.

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

Njira ndi async / await imamasuliridwa kukhala makina opangira malire ochenjera, omwe amabwereranso mkati mwake Task. Kudzera mu izi, mutha kudikirira kuti njirayo imalize, kuyimitsa, ndi china chilichonse chomwe mungachite ndi Task. Mkati mwa njirayo, makina a boma amawongolera kuphedwa. Mfundo yaikulu ndi yakuti ngati palibe kuchedwa, ndiye kuti kuphedwa ndikofanana, ndipo ngati kulipo, ndiye kuti ulusi umatulutsidwa. Kuti mumvetse bwino izi, ndi bwino kuyang'ana makina a boma awa. Mutha kupanga maunyolo kuchokera ku izi async / await njira.

Tiyeni tiyese. Ntchito ya afilosofi 100 pamakina okhala ndi ma cores 4 omveka, masekondi 8. Yankho lapitalo ndi Monitor linangopanga ulusi 4 woyambirira ndipo silinachite zina zonse. Iliyonse mwa ulusi 4wu inali yopanda ntchito pafupifupi 2ms. Ndipo yankho la async / kuyembekezera lidachita zonse 100, pafupifupi masekondi 6.8 kudikirira kulikonse. Zachidziwikire, m'machitidwe enieni, kukhala osagwira ntchito kwa masekondi 6 ndikosavomerezeka ndipo ndikwabwino kusakonza zopempha zambiri motere. Yankho lokhala ndi Monitor lidakhala kuti silinali loyipa konse.

Pomaliza

Monga mukuwonera pazitsanzo zazing'ono izi, .NET imathandizira zomanga zambiri zamalumikizidwe. Komabe, sizidziwika nthawi zonse momwe angagwiritsire ntchito. Ndikukhulupirira kuti nkhaniyi inali yothandiza. Tikukulunga izi pakadali pano, koma pali zinthu zambiri zosangalatsa zomwe zatsala, mwachitsanzo, zosonkhanitsira zotetezedwa ndi ulusi, TPL Dataflow, Reactive programming, Software Transaction model, etc.

Zotsatira

Source: www.habr.com

Kuwonjezera ndemanga