Filozofa voky tsara na Programming .NET Mifaninana

Filozofa voky tsara na Programming .NET Mifaninana

Andeha hojerentsika ny fomba fiasan'ny fandaharana miaraka sy mifanitsy amin'ny .Net, amin'ny fampiasana ny Olana Fisakafoana Filozofa ho ohatra. Ny drafitra dia izao, manomboka amin'ny fampifanarahana ny kofehy / dingana, mankany amin'ny modelin'ny mpilalao (amin'ireto ampahany manaraka ireto). Ny lahatsoratra dia mety ho ilaina ho an'ny olom-pantatra voalohany na mba hamelombelona ny fahalalanao.

Nahoana no manao izany mihitsy? Ny transistors dia mahatratra ny habeny kely indrindra, ny lalΓ n'i Moore dia miankina amin'ny famerana ny hafainganam-pandehan'ny hazavana ary noho izany dia hita ny fitomboana amin'ny isa, azo atao bebe kokoa ny transistors. Mandritra izany fotoana izany, mitombo ny habetsaky ny angon-drakitra, ary manantena valiny haingana avy amin'ireo rafitra ireo mpampiasa. Amin'ny toe-javatra toy izany, ny fandaharana "ara-dalΓ na", rehefa manana kofehy iray manatanteraka, dia tsy mandaitra intsony. Mila mamaha ny olan'ny famonoana miaraka na miaraka ianao. Ankoatra izany, ity olana ity dia misy amin'ny ambaratonga samihafa: amin'ny haavon'ny kofehy, amin'ny haavon'ny dingana, amin'ny haavon'ny milina ao amin'ny tambajotra (rafitra fizarana). .NET dia manana teknolojia avo lenta sy voasedra amin'ny fotoana mba hamahana haingana sy mahomby ny olana toy izany.

asa

Edsger Dijkstra dia nametraka io olana io tamin'ny mpianany tamin'ny fiandohan'ny taona 1965. Toy izao ny fandrafetana napetraka. Misy filozofa sasany (matetika dimy) ary mitovy ny isan'ny forks. Mipetraka eo amin'ny latabatra boribory izy ireo, misy sampana eo anelanelan'izy ireo. Ny filozofa dia afaka mihinana amin'ny loviany misy sakafo tsy misy farany, mieritreritra na miandry. Mba hihinana filozofa dia mila maka roa forks ianao (ny farany dia mizara ny fork amin'ny voalohany). Fihetseham-po roa samy hafa ny fakana sy fametahana tsofoka. Mangina ny filozofa rehetra. Ny asa dia ny fitadiavana algorithm toy izany izay hiheveran'izy rehetra sy ho feno na dia afaka 54 taona aza.

Voalohany, andeha isika hiezaka hamaha ity olana ity amin'ny fampiasana toerana iraisana. Mandry eo amin'ny latabatra mahazatra ny forks ary ny filozofa dia maka azy ireo fotsiny rehefa misy ary mamerina azy ireo. Eto misy olana amin'ny synchronization, rahoviana marina ny maka surebets? ahoana raha tsy misy fork? sns. Fa aleo aloha atomboka ny filozofa.

Mba hanombohana kofehy dia mampiasa dobo filomanosana izahay Task.Run fomba:

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

Ny dobo filomanosana dia natao hanatsarana ny famoronana sy famafana ny kofehy. Ity dobo ity dia manana filaharana misy asa ary ny CLR dia mamorona na manala kofehy arakaraka ny isan'ireo asa ireo. Dobo iray ho an'ny AppDomains rehetra. Tokony hampiasaina matetika io dobo io, satria. tsy mila manahiran-tsaina amin'ny famoronana, famafana ny kofehy, ny filaharany, sns. Azo atao tsy misy dobo, fa tsy maintsy mampiasa azy mivantana. Thread, Ity dia ilaina amin'ny tranga izay mila manova ny laharam-pahamehana amin'ny kofehy iray, rehefa misy asa maharitra, ho an'ny kofehy Foreground, sns.

Raha lazaina amin'ny fomba hafa, System.Threading.Tasks.Task mitovy ihany ny kilasy Thread, saingy miaraka amin'ny karazana fanamorana rehetra: ny fahafahana manatanteraka asa iray aorian'ny sakana amin'ny asa hafa, mamerina azy ireo amin'ny asany, manapaka azy ireo mora foana, sy ny maro hafa. sns. Izy ireo dia ilaina hanohanana ny fananganana async / miandry (Task-based Asynchronous Pattern, siramamy syntactic amin'ny fiandrasana ny fiasan'ny IO). Hiresaka momba izany isika any aoriana.

CancelationTokenSource eto dia ilaina mba hahafahan'ny kofehy mifarana amin'ny famantarana ny kofehy fiantsoana.

Olana fampifanarahana

Filozofa voasakana

Eny ary, haintsika ny mamorona kofehy, andeha isika hisakafo atoandro:

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

Eto aloha isika dia miezaka maka ny ankavia, ary avy eo ny havanana, ary raha mandaitra izany, dia mihinana ary mamerina azy ireo. Ny fakana fork iray dia atomika, i.e. kofehy roa dia tsy afaka maka iray amin'ny fotoana iray (diso: ny voalohany dia mivaky hoe maimaim-poana ny fork, ny faharoa - koa, ny voalohany maka, ny faharoa maka). Ho an'ity Interlocked.CompareExchange, izay tsy maintsy ampiharina miaraka amin'ny fampianarana processeur (TSL, XCHG), izay manidy fahatsiarovana ho an'ny famakiana sy fanoratana misesy atomika. Ary ny SpinWait dia mitovy amin'ny fananganana while(true) miaraka amin'ny "majika" kely fotsiny - ny kofehy dia maka ny processeur (Thread.SpinWait), fa indraindray mamindra ny fifehezana amin'ny kofehy hafa (Thread.Yeild) na matory (Thread.Sleep).

Saingy tsy mandeha io vahaolana io, satria tsy ho ela ny fikorianan'ny rano (ho ahy ao anatin'ny iray segondra) dia voasakana: ny filozofa rehetra dia maka ny ankaviany, fa tsy ny havanana. Ny array forks dia manana ny sandany: 1 2 3 4 5.

Filozofa voky tsara na Programming .NET Mifaninana

Ao amin'ny sary, fanakanana kofehy (deadlock). Green - famonoana, mena - synchronization, volondavenona - matory ny kofehy. Ny rhombuses dia manondro ny ora fanombohan'ny Asa.

Ny hanoanana ny filozofa

Na dia tsy ilaina ny mieritreritra indrindra sakafo, fa ny hanoanana mahatonga na iza na iza mahafoy filozofia. Andeha hojerentsika ny toe-javatra misy ny mosary amin'ny olana misy antsika. Ny hanoanana dia rehefa misy kofehy mandeha, fa tsy misy asa manan-danja, amin'ny teny hafa, io ihany no deadlock, izao ihany ny kofehy tsy matory, fa mavitrika mitady zavatra hohanina, nefa tsy misy sakafo. Mba hialana amin'ny fanakanana matetika dia haverinay ny fork raha tsy afaka maka iray hafa.

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

Ny zava-dehibe amin'ity fehezan-dalΓ na ity dia ny roa amin'ireo filozofa efatra no manadino ny mametraka ny sakeliny havia. Ary hita fa mihinana sakafo bebe kokoa izy ireo, fa ny hafa kosa manomboka mosarena, na dia mitovy ny laharam-pahamehana aza ny kofehy. Eto izy ireo dia tsy noana tanteraka, satria. Naverin'ny filozofa ratsy indraindray ny fitafiany. Hita fa ny olona tsara dia latsaka 5 heny noho ny ratsy. Noho izany, ny fahadisoana kely ao amin'ny code dia mitarika amin'ny fihenan'ny fampisehoana. Tsara ihany koa ny manamarika eto fa mety hisy toe-javatra tsy fahita firy rehefa miankavia avokoa ny filozofa rehetra, tsy misy havanana, miankavia, miandry, miankavia indray, sns. Io toe-javatra io koa dia mosary, toy ny fahatapahan-jiro. Tsy nahavita namerina izany aho. Ity ambany ity ny sarin'ny toe-javatra iray izay nahitana filozofa ratsy roa samy nandray ny forks ary ny roa tsara dia noana.

Filozofa voky tsara na Programming .NET Mifaninana

Eto ianao dia afaka mahita fa ny kofehy mifoha indraindray ary miezaka ny hahazo ny loharanon-karena. Ny roa amin'ireo cores efatra dia tsy manao na inona na inona (sary maitso etsy ambony).

Fahafatesan'ny filozofa

Eny ary, ny olana iray hafa izay mety hanelingelina ny fiaraha-misakafo be voninahitra ny filozofa dia raha maty tampoka ny iray amin'izy ireo miaraka amin'ny tsofoka eny an-tanany (ary handevina azy toy izany). Dia havela tsy hisakafo ny mpiara-monina. Azonao atao ny mamorona kaody ohatra ho an'ity tranga ity, ohatra, nariana NullReferenceException taorian'ny filozofa nandray ny forks. Ary, raha ny marina, ny fanavahana dia tsy horaisina ary ny kaody fiantsoana dia tsy hisambotra azy fotsiny (ho an'ity AppDomain.CurrentDomain.UnhandledException sy etc.). Noho izany dia ilaina ny mpitantana ny fahadisoana ao amin'ny kofehy ary miaraka amin'ny famaranana tsara.

mpandroso sakafo

Eny ary, ahoana no famahana ity olana ity, ny mosary ary ny fahafatesana? Filozofa iray ihany no avelantsika ho tonga any amin'ny garderie, ampio ny fifampiraharahana amin'ny kofehy ho an'ity toerana ity. Ahoana no hanaovana izany? Eritrereto hoe misy mpandroso sakafo eo akaikin'ireo filozofa izay manome alalana ny filozofa iray haka ny forks. Ahoana no hanaovana an'io mpandroso sakafo io sy ny fomba hanontanian'ny filozofa azy, mahaliana ny fanontaniana.

Ny fomba tsotra indrindra dia rehefa manontany tsy tapaka ny mpandroso sakafo ny filozofa mba hahazoana fidirana amin'ny forks. Ireo. ankehitriny ny filozofa dia tsy hiandry fork eo akaiky eo, fa miandry na manontany ny mpandroso sakafo. Amin'ny voalohany dia tsy mampiasa afa-tsy User Space izahay amin'izany, ao anatin'izany dia tsy mampiasa interrupts izahay hiantso ny fomba fiasa rehetra avy amin'ny kernel (momba azy ireo etsy ambany).

Vahaolana amin'ny habaka mpampiasa

Eto isika dia hanao toy ny fanaontsika tamin'ny fork iray sy filozofa roa, dia mihodina amin'ny tsingerina ary miandry. Fa izao dia ho filozofa daholo ary, toy ny hoe, iray ihany, i.e. azo lazaina fa ny filozofa naka an'io "fork volamena" io tamin'ny mpandroso sakafo ihany no hihinana. Amin'izany dia mampiasa SpinLock izahay.

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 ity dia mpisakana, miaraka amin'ny, amin'ny ankapobeny, mitovy while(true) { if (!lock) break; }, fa miaraka amin'ny "majika" bebe kokoa noho ny in SpinWait (izay ampiasaina any). Ankehitriny dia hainy ny manisa ireo miandry, mampatory azy ireo kely, sy ny maro hafa. sns. Amin'ny ankapobeny, manao izay rehetra azo atao mba hanatsarana. Saingy tsy maintsy tsaroantsika fa mbola ny tsingerina mavitrika izay mihinana ny loharanon'ny processeur ary mitazona ny fikorianan'ny rivotra, izay mety hitarika ho amin'ny hanoanana raha toa ka lasa laharam-pahamehana kokoa noho ny hafa ny filozofa iray, nefa tsy manana fork volamena (olana amin'ny Priority Inversion). . Noho izany, mampiasa azy io izahay amin'ny fanovana fohy dia fohy amin'ny fitadidiana zaraina, tsy misy antso an-tsarimihetsika, hidin-trano, ary tsy ampoizina hafa.

Filozofa voky tsara na Programming .NET Mifaninana

Manao sary ho an'ny SpinLock. Tsy mitsahatra ny "miady" ho an'ny fork volamena ny sakelidrano. Misy ny tsy fahombiazana - eo amin'ny tarehimarika, ny faritra voafantina. Tsy ampiasaina tanteraka ny cores: eo amin'ny 2/3 eo ho eo amin'ireo kofehy efatra ireo.

Vahaolana iray hafa eto dia ny fampiasana fotsiny Interlocked.CompareExchange miaraka amin'ny fiandrasana mavitrika mitovy amin'ny aseho amin'ny fehezan-dalΓ na etsy ambony (ao amin'ireo filozofa mosarena), fa izany, araka ny efa voalaza, dia mety hitarika amin'ny fanakanana.

amin'ny Interlocked Marihina fa tsy misy ihany CompareExchange, fa koa fomba hafa amin'ny famakiana sy fanoratana atomika. Ary amin'ny alΓ lan'ny famerimberenan'ny fanovana, raha misy kofehy hafa manana fotoana hanovana ny fanovana (vakio 1, vakio 2, soraty 2, soraty ny 1 dia ratsy), dia azo ampiasaina amin'ny fanovana sarotra amin'ny sanda tokana (modelin'ny Interlocked Anything).

Kernel Mode Solutions

Mba hialana amin'ny fandaniam-bola amin'ny loop, andeha hojerentsika ny fomba hanakanana kofehy iray. Raha lazaina amin'ny teny hafa, ny fanohizana ny ohatra asehontsika dia andeha hojerentsika ny fomba nampatorian'ny mpandroso sakafo ilay filozofa ary mamoha azy rehefa ilaina izany. Voalohany, andeha hojerentsika ny fomba hanaovana izany amin'ny alΓ lan'ny fomba kernel an'ny rafitra miasa. Ny rafitra rehetra ao dia matetika miadana kokoa noho ny ao amin'ny habaka mpampiasa. Imbetsaka miadana, ohatra AutoResetEvent mety in-53 miadana kokoa SpinLock [Richter]. Saingy miaraka amin'ny fanampian'izy ireo dia azonao atao ny mampifanaraka ny dingana manerana ny rafitra, voatanisa na tsia.

Ny fanorenana fototra eto dia ny semafora naroson'i Dijkstra efa ho antsasaka taona lasa izay. Ny semaphore dia, raha tsorina, dia isa tsara tantanin'ny rafitra, ary asa roa eo aminy, ny fampiakarana sy ny fampihenana. Raha toa ka tsy mihena izany, dia aotra, dia voasakana ny kofehy fiantsoana. Rehefa ampitomboina amin'ny kofehy / dingana mavitrika hafa ny isa, dia adino ny kofehy ary ahena indray ny semaphore amin'ny isa lany. Afaka maka sary an-tsaina ny fiarandalamby ao anaty tavoahangy misy semafora. .NET dia manolotra fananganana maromaro miaraka amin'ny fiasa mitovy: AutoResetEvent, ManualResetEvent, Mutex ary ny tenako Semaphore. Hampiasaintsika AutoResetEvent, ity no tsotra indrindra amin'ireo fanorenana ireo: sanda roa ihany 0 sy 1 (diso, marina). Ny fombany WaitOne() manakana ny kofehy fiantsoana raha 0 ny sandany, ary raha 1, dia ampidinina ho 0 izany ary aingana izany. Fomba iray Set() miakatra ho 1 ary mamela ny mpandroso sakafo iray mandalo, izay midina indray ho 0. Miasa toy ny fiviliana metro.

Andao hanasarotra ny vahaolana ary ampiasao ny hidin-trano ho an'ny filozofa tsirairay, fa tsy ho an'ny rehetra indray mandeha. Ireo. ankehitriny dia mety hisy filozofa maromaro indray mandeha, fa tsy iray. Saingy nosakananay indray ny fidirana amin'ny latabatra mba hahazoana tsara, hisorohana ny hazakazaka (fepetra hazakazaka), haka 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();
}

Mba hahatakarana ny zava-mitranga eto, diniho ny tranga rehefa tsy nahavita ny filozofa, dia ho toy izao manaraka izao ny fihetsiny. Miandry ny fidirana amin'ny latabatra izy. Rehefa nahazo izany izy, dia miezaka ny maka ny tsofoka. Tsy nahomby. Izy io dia manome fahafahana miditra amin'ny latabatra (fanilihana iombonana). Ary mandalo ny "turnstile" (AutoResetEvent) (misokatra aloha izy ireo). Miditra amin'ny tsingerina indray izany, satria tsy manana forks izy. Miezaka maka azy ireo izy ary mijanona eo amin'ny "turnstile" azy. Ny mpifanolobodirindrina tsara kokoa amin'ny havanana na ankavia, rehefa avy nisakafo, dia manokatra ny filozofantsika, "manokatra ny turnstile azy." Ny filozofantsika dia mandalo izany (ary mihidy ao aoriana) fanindroany. Niezaka fanintelony izy haka ny tsofoka. Mirary anao ho tsara vintana. Ary mandalo ny fiolahanany izy mba hisakafo.

Rehefa misy hadisoana kisendrasendra ao amin'ny kaody toy izany (misy foana izy ireo), ohatra, ny mpifanolo-bodirindrina iray dia tsy voafaritra tsara na ny zavatra iray ihany no noforonina. AutoResetEvent ho an'ny rehetra (Enumerable.Repeat), dia hiandry ny mpamorona ny filozofa, satria Asa sarotra ny fitadiavana lesoka amin'ny code toy izany. Ny olana hafa amin'ity vahaolana ity dia tsy manome antoka fa tsy ho noana ny filozofa sasany.

Vahaolana Hybrid

Nijery fomba roa momba ny fotoana izahay, rehefa mijanona amin'ny fomba mpampiasa sy ny loop, ary rehefa manakana ny kofehy amin'ny kernel. Ny fomba voalohany dia tsara ho an'ny hidin-trano fohy, ny faharoa ho an'ny lava. Matetika dia ilaina ny miandry kely aloha ny fiovan'ny fiovan'ny tadivavarana, ary avy eo sakanana ny kofehy rehefa lava ny fiandrasana. Ity fomba fiasa ity dia ampiharina amin'ny antsoina hoe. rafitra hybrid. Ireto misy fananganana mitovy amin'ny mode kernel, saingy miaraka amin'ny loop mode mpampiasa: SemaphorSlim, ManualResetEventSlim sns Ny endrika malaza indrindra eto dia Monitor, satria ao amin'ny C# dia misy fanta-daza lock syntax. Monitor ity dia semaphore mitovy amin'ny sanda ambony indrindra 1 (mutex), fa miaraka amin'ny fanohanana ny fiandrasana amin'ny loop, recursion, ny lamina Condition Variable (bebe kokoa amin'ny etsy ambany), sns. Andeha hojerentsika ny vahaolana miaraka aminy.

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

Eto indray isika dia manakana ny latabatra iray manontolo ho an'ny fidirana amin'ny garderie, fa izao dia mamoha ny kofehy rehetra indray mandeha, fa tsy ny mpiara-monina rehefa misy olona misakafo. Ireo. aloha, misy mihinana sy manakana ny mpiara-monina, ary rehefa vita io, fa te-hisakafo indray avy hatrany, dia miditra amin'ny fanakanana sy mamoha ny mpiara-monina aminy, satria. kely kokoa ny fotoana fiandrasana azy.

Izany no fomba ialana amin'ny fahatapahan-jiro sy ny hanoanana ny filozofa sasany. Mampiasa tadivavarana izahay mandritra ny fiandrasana fohy ary sakanana ny kofehy mandritra ny fotoana maharitra. Ny famahana ny olon-drehetra indray mandeha dia miadana kokoa noho ny hoe ny mpifanolo-bodirindrina ihany no voasakana, toy ny amin'ny vahaolana amin'ny AutoResetEvent, fa ny fahasamihafana dia tsy tokony ho lehibe, satria Tokony hijanona amin'ny fomba mpampiasa aloha ny kofehy.

Π£ lock misy tsy ampoizina ratsy ny syntax. Manoro hevitra hampiasa Monitor mivantana [Richter] [Eric Lippert]. Ny iray amin'izy ireo dia izany lock mivoaka foana Monitor, na dia nisy aza ny maningana, ka mety hanova ny toetry ny fahatsiarovana iombonana ny kofehy hafa. Amin'ny toe-javatra toy izany, matetika dia tsara kokoa ny mandeha any amin'ny deadlock na amin'ny fomba ahoana no hamarana ny fandaharana. Ny mahagaga iray hafa dia ny Monitor mampiasa sakana synchronization (SyncBlock), izay misy amin'ny zavatra rehetra. Noho izany, raha misy zavatra tsy mety voafantina, dia afaka mora foana ny fahatapahan-jiro (ohatra, raha mihidy amin'ny tady interned). Ampiasainay ny zavatra miafina foana amin'izany.

Ny lamina Condition Variable dia ahafahanao mampihatra amin'ny fomba fohy kokoa ny andrasan'ny toe-javatra sarotra sasany. Ao amin'ny .NET dia tsy feno, raha ny hevitro, satria Amin'ny teoria dia tokony hisy filaharana maromaro amin'ny variables maromaro (toy ny ao amin'ny Posix Threads), fa tsy amin'ny lok iray. Avy eo dia afaka manao azy ireo ho an'ny filozofa rehetra. Saingy na dia amin'ity endrika ity aza dia ahafahanao mampihena ny code.

filozofa maro na async / await

Eny ary, afaka manakana kofehy isika izao. Ahoana anefa raha manana filozofa maro isika? 100? 10000? Ohatra, nahazo fangatahana 100000 ho an'ny mpizara tranonkala izahay. Ho ambony loha ny famoronana kofehy ho an'ny fangatahana tsirairay, satria be dia be ny kofehy tsy hihazakazaka mifanitsy. Hazakazaka ihany araka izay misy cores lojika (manana 4 aho). Ary ny olon-kafa rehetra dia haka ny loharanon-karena. Ny vahaolana iray amin'ity olana ity dia ny async / wait pattern. Ny heviny dia tsy mitazona ny kofehy ny fiasa raha toa ka mila miandry zavatra hitohy. Ary rehefa manao zavatra izy dia manohy ny famonoana azy (fa tsy voatery amin'ny kofehy iray ihany!). Amin'ny tranga misy antsika dia hiandry ny fork isika.

SemaphoreSlim manana izany WaitAsync() fomba. Ity misy fampiharana mampiasa ity lamina ity.

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

Fomba miaraka amin'ny async / await dia nadika ho milina fanjakana sarotra izay mamerina avy hatrany ny ao anatiny Task. Amin'ny alΓ lan'izany dia afaka miandry ny fahavitan'ny fomba ianao, manafoana azy, ary ny zavatra hafa rehetra azonao atao amin'ny Task. Ao anatin'ny fomba, ny milina fanjakana no mifehy ny famonoana. Ny tsipika ambany dia ny hoe raha tsy misy fanemorana, ny famonoana dia synchronous, ary raha misy, dia avoaka ny kofehy. Mba hahatakarana tsara kokoa an'io, tsara kokoa ny mijery an'io milina fanjakana io. Afaka mamorona rojo avy amin'ireo ianao async / await fomba.

Andao hizaha toetra. Ny asan'ny filozofa 100 amin'ny milina misy cores lojika 4, 8 segondra. Ny vahaolana teo aloha miaraka amin'ny Monitor ihany no nihazakazaka ny kofehy 4 voalohany ary ny ambiny dia tsy nandeha mihitsy. Ny tsirairay amin'ireo kofehy 4 ireo dia tsy miasa mandritra ny 2ms eo ho eo. Ary ny vahaolana async / miandry dia nihazakazaka 100 rehetra, miaraka amin'ny fiandrasana 6.8 segondra isaky ny tsirairay. Mazava ho azy, amin'ny rafitra tena izy dia tsy azo ekena ny idle mandritra ny 6 segondra ary tsara kokoa ny tsy manao fangatahana be dia be toy izany. Ny vahaolana miaraka amin'ny Monitor dia hita fa tsy azo ekena mihitsy.

famaranana

Araka ny hitanao avy amin'ireo ohatra kely ireo, ny .NET dia manohana fananganana synchronization maro. Na izany aza, tsy mazava foana ny fomba fampiasana azy ireo. Manantena aho fa nanampy ity lahatsoratra ity. Amin'izao fotoana izao dia izao no farany, fa mbola misy zavatra mahaliana maro tavela, ohatra, fanangonana kofehy azo antoka, TPL Dataflow, fandaharana Reactive, modely Transaction Software, sns.

loharanom-baovao

Source: www.habr.com

Add a comment