H Philosophos bene aut Aliquam .NET Programming

H Philosophos bene aut Aliquam .NET Programming

Intueamur quomodo concurrentes et paralleli opera programmandi in .Net, philosophorum prandiorum exemplo utentes. Consilium hoc est, a sequela/processus synchronisation ad exemplar actoris (in partibus sequentibus). Articulus potest esse utilis ad primam noticiam vel ad tuam scientiam reficiendam.

Cur etiam hoc facere norunt? Transistores suam minimam magnitudinem attingunt, lex Moore terminum celeritatis lucis ferit, et ideo incrementum in numeris observatur: plures transistores fieri possunt. Eodem tempore, summa notitiarum incrementa, et utentes exspectant responsum a rationibus immediatum. In tali condicione programmatis "normalis", cum unum filum exsequens habemus, iam non est efficax. Non opus est aliquo modo quaestionem solvere de executione simultanei vel concurrentis. Quaestio autem haec in diversis gradibus existit: in gradu sequelae, in gradu processus, in machinis in retis (ratio distributa). . NET summus qualitas, technologiae probatae pro cito et efficaciter solvendas huiusmodi difficultates habet.

negotium

Edsger Dijkstra quaestionem discipulis suis anno 1965 retraxit. Statuta formula talis est. Sunt quidam philosophorum numero totidemque tridentes. Sedent ad mensam rotundam, inter eas tridentes. Philosophi de laminis infinitis cibis vesci possunt, cogitare vel expectare. Comedere debet philosophus duas furcas, haec furcam cum illa communicat. Electio et impositio furca sunt duae actiones separatae. Omnes philosophi tacent. Negotium est talem algorithmum invenire ut omnes cogitent et etiam post 54 annos bene alantur.

Primum, conemur hanc quaestionem solvere utendo spatio communi. Fuscinulae in communi mensa iacent, et philosophi simpliciter sumunt eas, cum ibi sunt, et eas reponunt. Unde haec problemata synchronisationi oriuntur, quando exacte ad tridentes capiuntur? quid faciam si non est obturaculum? etc. Sed primum a philosophis ordiamur.

Ad fila incipere filo utimur per piscinam Task.Run modum;

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

Stagnum sequela ad optimize creationem et remotionem staminum destinatur. Haec piscina quenam officiorum habet et CLR fila gignit vel delet secundum numerum horum operum. Una piscina omnibus AppDomains. Id lacus sit amet semper fere, nam... non opus est molestare cum fila creando et delendo, earum queues, etc. Facere potes sine piscina, sed tunc habebis ea directe. Thread, hoc utile est in casibus, cum filo prioritatem mutare necesse est, cum longam habemus operationem, pro filo fabricatorio, etc.

In aliis verbis, System.Threading.Tasks.Task genus idem Threadsed cum omni commodorum commodo: facultas inferendi negotium post stipitem aliorum munerum, eas a muneribus, et multo magis commode interpellet. etc. Opus sunt ad constructiones async/exspectandas (Asynchronum Negotium fundatum Exemplum, saccharum syntacticum ad operationes IO exspectandas). De hoc postea dicemus.

CancelationTokenSource hic necesse est ut signum e filo se terminet.

Sync Exitus

Clausus philosophorum

Bene, Scimus fila creare, prandium experiamur;

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

Hic primum tentamus sumendo laevam, deinde furcam dextram, et si operatur, comedimus et reducimus; Furca sumens est atomica, i.e. duo stamina non possunt unum simul capere (iniuria: prima legit furca liberam, secunda eadem facit, prima capit, altera sumit). Quia haec Interlocked.CompareExchange, quae instrui debet per processum instructionem .TSL, XCHG) , quod particulam memoriae pro atomicis sequentiae lectionis et scribendi genere comprimit. Et SpinWait valet ad constructionem while(true) solum cum paulo "magicae" - stamina processus tollit (Thread.SpinWaitsed interdum in aliud linum imperium transit ( . )Thread.Yeild) Vel obdormivit (Thread.Sleep).

Sed haec solutio non operatur, quia. stamina mox (intra secunda mihi) praeclusa sunt: ​​laeva furca philosophi omnes capiunt, sed non est rectum. Fuscinulae deinde ordinatae sunt valores: 1 2 3 4 5 .

H Philosophos bene aut Aliquam .NET Programming

In tabula interclusio staminum (deadlock). Viridis supplicium indicat, rubra synchronisationem indicat, et griseum filum indicat dormientem. Crystallini designant Lorem tempus Tasks.

Fames Philosophorum

Quamvis non multum cibi ad cogitandum opus sit, fames aliquem philosophiam dare potest. Studeamus in problemate nostro famem sequelae condicionem simulare. Fames est cum filum operatur, sed sine opere significanti aliis verbis idem est lock, nunc tantum filum non dormit, sed active quaerit aliquid edendi, sed cibus non est. Ad frequentes interclusiones vitandas, reducemus bifurcam, si alteram capere non potuimus.

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

Gravis est in hoc codice duo philosophorum quattuor philosophorum laevam furcam ponere. Et evenit ut plus comedant cibum, et alii incipiant esurire, licet stamina tantundem habeant prioritatem. Hic non sunt ieiunantes, quia... mali philosophi interdum tridentes reponunt. Evenit ut boni minus quam mali 5 edant. Ita parvus error in codice effectus guttam ducit. Hic etiam notandum est, quod rarum situm esse potest, cum omnes philosophi levant sinistrum, non est dextrum unum, ponunt sinistrum unum, expecta, sume sinistrum iterum, etc. Haec condicio est etiam famis, venenatis mutui similior. Non potui repetere. Subter pictura est in qua duo mali philosophi utrumque tridentem sumpserunt, et duo boni ieiunaverunt.

H Philosophos bene aut Aliquam .NET Programming

Hic videre potes quod stamina interdum excitare et opum acquirere conantur. Duo ex quattuor coros nihil faciunt (graphi viridis supra).

Mors Philosophi

Illud magis dubium, quod gloriosum philosophorum convivium interpellare potuit, si unus ex illis in manibus furcis subito moritur (et sic sepelietur). Tum vicini sine prandio relinquentur. Potes adire cum exempli causa in hoc ipso casu, exempli gratia, abjectus est NullReferenceException postquam philosophus sumit tridentes. Et obiter, exceptio non tractabitur, et vocatio codicem non simpliciter capiet (hoc enim AppDomain.CurrentDomain.UnhandledException et etc.). Ergo erroris tracto opus est in ipsis filis et cum lepide terminatione.

Famulus potionem

Bene, quomodo solvemus hanc quaestionem de mortibus, inedia, et mortibus? Unum tantum philosophum patiemur ad tridentes, et exclusionem staminum huic loco addamus. Quomodo facere? Finge proximum philosophos esse ministratorem qui permittit uni philosopho tridentem sumere. Quomodo hunc architriclinum facere debeamus et quomodo philosophi ab eo petant quaestiones sunt iucundae.

Simplicissima via est philosophorum ut simpliciter constanter peteret architriclinus aditum ad tridentes. Illae. Nunc philosophi non exspectant furcam prope, sed expecta vel rogant architriclinus. Primo tantum Spatium User ad hoc utimur, in eo non obloquiis utimur aliquem processum a nucleo vocare (plura de his infra).

User spatium solutiones

Hoc idem faciemus, quod ante fecimus cum una furca et duobus philosophis, nos in ansa trahere et expectare. Nunc vero omnes philosophi et quasi furca una, i.e. solum philosophum possumus dicere, qui hoc "furca aurea" ab architriclino sumpsit. Ad hoc utimur 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 hoc obstructus est, cum fere loquendo idem while(true) { if (!lock) break; }sed magis magia quam in SpinWait (quae ibi). Nunc novit computare expectantes, paululum et plus dormire. etc. In genere omnia facit ad optimize possibilia. Sed tenendum est hanc adhuc eandem esse ansam activam quae processus facultates exedens et filum tenet, quod ad famem ducere potest si unus philosophorum prior fit quam ceteri, sed furcam auream non habet. ). Propterea utimur eo modo ad brevissimas mutationes communium memoriarum, sine ulla tertia-pars appellatio, comae nestae, vel aliae admirationis.

H Philosophos bene aut Aliquam .NET Programming

drawing for SpinLock. Rivi constanter "pugnant" pro furca aurea. Defectus occurrunt - area clara in figura. Cores plene non utuntur: ab his stamina circiter 2/3 quatuor tantum.

Alia solutio hic erit ad solum usum Interlocked.CompareExchange cum eadem activa exspectatione, ut in codice supra (in philosophorum fame pereo), hoc autem, ut iam dixi, obsisteret theoretice posset.

in Interlocked valet dicere quod non solum CompareExchangesed etiam alii methodi legendi atomi ET scribendi. Et iterando mutationem, si alius stamina efficit ut suas mutationes faciat (legere 1, lege 2, scribe 2, scribe 1 malum est), potest adhiberi ad varias mutationes ad unum valorem (Interlocked Quidquid exemplaris).

Kernel modus solutionum

Ut facultates absumptis in ansa, inspiciamus quomodo filo intercluderetur. Id est, exemplo nostro continuato, videamus quomodo philosophum architriclinus dormiat et evigilet, nisi cum necesse fuerit. Primum videamus quomodo hoc facere per modum nuclei operandi ratio. Omnes structurae ibi saepe desinunt tardiores esse quam in spatio usoris. Saepius tardius, e.g AutoResetEvent maybe LIII temporibus tardius SpinLock [Richter]. Sed cum eorum auxilio, processus per totam rationem synchronize potes, vel non administrari.

Praecipuum consilium hic semaphorum est, quod ante Dijkstra plus quam dimidium saeculum proponitur. Semaphora simpliciter ponitur, integer affirmativus systematis regitur et duae operationes in eo - augmentum et diminutio. Si nulla res minui non potest, vocationis linum impeditur. Cum numerus alio activo filo/processui augetur, tum stamina transeunt et semaphorum numerus transmissus iterum minuitur. Impedimenta in ampulla cum semaphore fingere potes. .NET plura construit cum similibus functionality: AutoResetEvent, ManualResetEvent, Mutex ipsum Semaphore. Nos utemur AutoResetEventhaec constructio simplicissima est: duo tantum valores 0 et 1 (falsus, verus). Methodus eius WaitOne() stamina dictamen impedit, si valor 0 erat, et si 1, eum ad 0 deponit et saltat. Methodus Set() crescit ad 1 and lets one person through, who again decrescat 0. Acts like turnstile in the subway.

Inpediamus solutionem et interclusionem pro unoquoque philosopho utamur, et non semel. Illae. Sed plures philosophi simul possunt manducare, et non unum. Sed rursus aditum ad mensam impedimus ut tridentes recte capiamus, condiciones generis vitantes.

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

Ad intelligendum quid hic aguntur, considera quando philosophus neglexerit tridentem, actiones eius sic erunt. Aditus ad mensam exspectat. Accepto tridente conatur. Non elaborare. Dat aditum ad mensam (mutua exclusio). Et transit suum "turturem" (AutoResetEvent) (primo patent). iterum in cyclum cadit, quia Non habet fuscinulas. Eas capere conatur et ad suum "turstile" sistit. Aliquis felicior vicinus dextra laevaque edens, philosophum nostrum aperiendo turturem. Noster philosophus secundo percurrit. Tertium tridentem sumere conatur. Prosperum. Atque it per turntilem prandere.

Cum in tali codice temere errores insunt (qui semper exstant), exempli gratia vicinus perperam specificetur vel idem obiectum creabitur. AutoResetEvent omnibus (Enumerable.Repeat) philosophi tincidunt exspectabunt, quia Errores in tali codice invenire admodum difficile est. Alia quaestio huius solutionis est quod non praestat aliquem philosophum non esurire.

Hybrid solutiones

Duos aditus synchronisationi inspeximus, cum in usuario modo manemus et in ansa subtegimus et cum filum per nucleum intercludimus. Primus modus prodest brevibus caudices, secundus longis. Saepe debes primum breviter exspectare variationem mutandi in ansa, et deinde stamina obstrue cum longa mora est. Aditus ad effectum sic dictum. hybrid designs. Eadem constructio quae in nucleo modo habet, nunc cum ansa utentis; SemaphorSlim, ManualResetEventSlim Maxime populare consilium hic est etc Monitor, quod in C # ibi notus lock syntaxis. Monitor hoc idem semaphore cum maximo valore 1 (mutex), sed cum auxilio exspectationis in ansa, recursione, conditione variabili exemplari (more infra) etc. Inspiciamus solutionem cum ea.

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

Hic rursus totam mensam impedimus ne ad uncinos accessus, nunc omnia fila statim absolvimus, quam vicini cum quis edendi finit. Illae. Uno modo, quis proximos manducat et impedit, et cum hoc aliquis finit, sed statim vult iterum comedere, vadit in scandalum et evigilat proximos suos, quia. morae tempus minus est.

Sic mortifera et inedia alicuius philosophi vitamus. Ansa utimur ad breve tempus exspectandum et filum diu obstruunt. Expeditio omnis statim tardior est quam si modo proximus erat apertus, ut in solutione AutoResetEventsed differentia non debet esse magna, quia relatorum debet in user modus primus.

Π£ lock syntaxin habet aliquid ingratum elit. Commendatur ut Monitor directe [Richter] [Eric Lippert]. Unus eorum est lock semper exeat Monitoretiam si exceptio facta est, et tunc aliud filum potest mutare statum comunis memoriae. His in casibus, saepe melius est in mortificatione vel in aliquo progressione tuto terminari. Aliud mirum est quod Monitor horologium utitur caudices (SyncBlock) quae sunt in omnibus obiectis. Si ergo objectum indebitum deligatur, facile potes deadlock (exempli gratia, si filo interposita claudas). Ad hoc occultum semper utimur.

Conditio Variabilis exemplaris permittit ut brevius efficiatur exspectatio alicuius condicionis implicatae. In .NET imperfecta est, opinor, quia... In theoria, plures esse debent queues in pluribus variabilibus (ut in Threads Posix), non in uno lock. Posset etiam facere omnes philosophos. Sed etiam in hac forma codicem tibi permittit breviare.

Multi philosophorum or async / await

Bene, nunc efficaciter fila impedire possumus. Sed quid, si multum philosophorum habemus? 100? 10000? Exempli gratia: 100000 petitiones interretiali servo recepimus. Pro singulis stamina creandis carus erit, quia tot stamina non erunt in parallela facienda. Tantummodo totidem nuclei logici exsecutioni mandabuntur (I have 4). Et omnes alii facultates simpliciter auferent. Una solutionis huius problematis est async / exspectatio exemplaris. Idea est quod munus non tenet linum si opus est exspectare aliquid pergere. Et cum aliquid fit, supplicium repetit (non autem necessario in eodem filo). In casu nostro fuscinulam exspectabimus.

SemaphoreSlim habet hoc WaitAsync() modum. Hic est exsecutio exemplaris usus.

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

Modus cum async / await in machinam finitam versutam translatum est, quae statim reddit internum suum Task. Per eam, potes expectare modum complendi, destruendi, et omnia alia quae facere cum Negotium potes. Intra modum, machina publica exsecutionem moderatur. In ima linea est, ut si nulla mora sit, supplicium synchronum est, et, si est, tum stamen solutum est. Ad meliorem huius intelligentiam, melius est intueri hanc rem publicam. Vincula ex his potes creare async / await modis.

Experiamur. Opus 100 philosophorum super machinam cum 4 zoologicis logicis, 8 secundis. Praevia solutio cum Monitor primas 4 stamina tantum perfecit et reliquas omnino non exercuit. Uterque horum 4 stamina ociosus erat circiter 2ms. Et async / exspectatio solutionis omnes 100 fecit, cum mediocris 6.8 secundis singulis exspectantibus. Utique in realibus systematibus, otiosis pro 6 secundis, inconveniens est et melius est non tot petitiones hoc modo procedere. Solutio cum Monitor evasit omnino non scalabilem esse.

conclusio,

Ut ex his parvis exemplis videre potes, .NET subsidia multa constructio synchronisationi. Sed non semper patet quomodo utatur. Spero hunc articulum utile fuisse. Hoc nunc involvimus, sed adhuc multum superest supellectilem interesting, exempli gratia, collectiones fila tuta, TPL Dataflow, programmatio reactiva, exemplar Transactionis Software, etc.

fontibus

Source: www.habr.com