Awọn Philosophers ti o ni ifunni daradara tabi Eto NET ifigagbaga

Awọn Philosophers ti o ni ifunni daradara tabi Eto NET ifigagbaga

Jẹ ki a wo bii siseto ibaraenisọrọ ati ibaramu ṣiṣẹ ni .Net, ni lilo apẹẹrẹ ti iṣoro awọn ọlọgbọn ọsan. Eto naa jẹ atẹle yii, lati okun / amuṣiṣẹpọ ilana si awoṣe oṣere (ni awọn apakan atẹle). Nkan naa le wulo fun ojulumọ akọkọ tabi lati sọ imọ rẹ sọtun.

Kini idi ti paapaa mọ bi a ṣe le ṣe eyi? Awọn transistors ti de iwọn ti o kere ju, ofin Moore n kọlu opin iyara ti ina, nitorinaa a ṣe akiyesi idagbasoke ni awọn nọmba; Ni akoko kanna, iye data n dagba, ati pe awọn olumulo n reti esi lẹsẹkẹsẹ lati awọn eto. Ni iru ipo bẹẹ, siseto “deede”, nigba ti a ba ni okun ipaniyan kan, ko munadoko mọ. A nilo lati bakan yanju isoro ti igbakana tabi ipaniyan nigbakanna. Pẹlupẹlu, iṣoro yii wa ni awọn ipele oriṣiriṣi: ni ipele o tẹle ara, ni ipele ilana, ni ipele ti awọn ẹrọ lori nẹtiwọki (awọn ọna ṣiṣe ti a pin). NET ni awọn imọ-ẹrọ ti o ni agbara-giga, akoko idanwo fun ni kiakia ati ṣiṣe awọn iṣoro iru awọn iṣoro.

Nkan

Edsger Dijkstra beere iṣoro yii si awọn ọmọ ile-iwe rẹ pada ni 1965. Ilana ti iṣeto ni bi atẹle. Nọmba kan wa (nigbagbogbo marun) ti awọn onimọ-jinlẹ ati nọmba kanna ti orita. Wọn joko ni tabili yika, awọn orita laarin wọn. Awọn onimo ijinlẹ sayensi le jẹ ninu awọn awo wọn ti ounjẹ ailopin, ronu tabi duro. Láti jẹun, onímọ̀ ọgbọ́n orí gbọ́dọ̀ mú oríta méjì (èyí tí ó kẹ́yìn pín oríta pẹ̀lú àkọ́kọ́). Gbigbe ati fifi orita silẹ jẹ awọn iṣe lọtọ meji. Gbogbo awọn ọlọgbọn ni ipalọlọ. Iṣẹ-ṣiṣe ni lati wa iru algorithm kan ki gbogbo wọn ronu ati pe wọn jẹun daradara paapaa lẹhin ọdun 54.

Ni akọkọ, jẹ ki a gbiyanju lati yanju iṣoro yii nipa lilo aaye pinpin. Awọn orita dubulẹ lori tabili ti o wọpọ ati awọn onimọ-jinlẹ mu wọn nirọrun nigbati wọn ba wa nibẹ ati fi wọn pada. Eyi ni ibiti awọn iṣoro pẹlu amuṣiṣẹpọ dide, nigbawo ni pato lati mu awọn orita? Kini lati ṣe ti ko ba si plug? bbl Ṣugbọn akọkọ, jẹ ki a bẹrẹ pẹlu awọn ọlọgbọn.

Lati bẹrẹ awọn okun a lo adagun okun nipasẹ Task.Run ọna:

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

A ṣe apẹrẹ adagun okun okun lati mu ẹda ati yiyọ awọn okun pọ si. Adagun adagun-odo yii ni isinyi ti awọn iṣẹ ṣiṣe ati CLR ṣẹda tabi paarẹ awọn okun ti o da lori nọmba awọn iṣẹ ṣiṣe wọnyi. Ọkan pool fun gbogbo AppDomains. O yẹ ki o lo adagun-odo yii nigbagbogbo, nitori ... ko si ye lati ṣe wahala pẹlu ṣiṣẹda ati piparẹ awọn okun, awọn ila wọn, bbl O le ṣe laisi adagun kan, ṣugbọn lẹhinna o yoo ni lati lo taara Thread, Eyi jẹ iwulo fun awọn ọran nigba ti a nilo lati yi ayo ti okun kan pada, nigba ti a ba ni iṣẹ pipẹ, fun okun iwaju, ati bẹbẹ lọ.

Ni awọn ọrọ miiran, System.Threading.Tasks.Task kilasi jẹ kanna Thread, ṣugbọn pẹlu gbogbo iru awọn irọrun: agbara lati ṣe ifilọlẹ iṣẹ-ṣiṣe kan lẹhin bulọọki ti awọn iṣẹ-ṣiṣe miiran, da wọn pada lati awọn iṣẹ, ni irọrun da wọn duro, ati pupọ diẹ sii. bbl Wọn nilo lati ṣe atilẹyin async / durode awọn ikole (Apẹẹrẹ Asynchronous ti o da lori iṣẹ-ṣiṣe, suga syntactic fun iduro fun awọn iṣẹ IO). A yoo sọrọ nipa eyi nigbamii.

CancelationTokenSource nibi o jẹ dandan pe o tẹle ara le fopin si ara rẹ lori ifihan agbara lati okun ipe.

Awọn ọrọ amuṣiṣẹpọ

Dina philosophers

O dara, a mọ bi a ṣe le ṣẹda awọn okun, jẹ ki a gbiyanju ounjẹ ọsan:

// Кто какие вилки взял. К примеру: 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();
    }
}

Nibi a kọkọ gbiyanju lati mu apa osi ati lẹhinna orita ọtun, ati pe ti o ba ṣiṣẹ, a jẹun ati fi wọn pada. Gbigba orita kan jẹ atomiki, i.e. okun meji ko le gba ọkan ni akoko kanna (aṣiṣe: akọkọ ka pe orita jẹ ofe, ekeji ṣe kanna, akọkọ gba, ekeji gba). Fun eyi Interlocked.CompareExchange, eyiti o gbọdọ ṣe imuse nipa lilo itọnisọna ero isise (TSL, XCHG), eyiti o tii iranti nkan kan fun kika ati kikọ atomiki lẹsẹsẹ. Ati SpinWait jẹ deede si ikole while(true) nikan pẹlu “idan” kekere kan - okun gba ero isise naa (Thread.SpinWait), ṣugbọn nigbamiran yoo kọja iṣakoso si okun miiran (Thread.Yeild) tabi sun oorun (Thread.Sleep).

Ṣugbọn ojutu yii ko ṣiṣẹ, nitori ... awọn okun laipẹ (laarin iṣẹju kan fun mi) ti dina: gbogbo awọn onimọ-jinlẹ mu orita osi wọn, ṣugbọn ko si ọkan ti o tọ. Eto orita lẹhinna ni awọn iye: 1 2 3 4 5.

Awọn Philosophers ti o ni ifunni daradara tabi Eto NET ifigagbaga

Ninu aworan, idinamọ awọn okun (titiipa). Alawọ ewe tọkasi ipaniyan, pupa tọkasi amuṣiṣẹpọ, ati grẹy tọkasi okun ti n sun. Awọn okuta iyebiye tọkasi akoko ifilọlẹ ti Awọn iṣẹ-ṣiṣe.

Ebi Awon Omoye

Botilẹjẹpe o ko nilo ounjẹ pupọ lati ronu, ebi le fi ipa mu ẹnikẹni lati fi imọ-jinlẹ silẹ. Jẹ ká gbiyanju lati fara wé awọn ipo ti okun ifebipani ninu isoro wa. Ebi jẹ nigbati okun ba ṣiṣẹ, ṣugbọn laisi iṣẹ pataki, ni awọn ọrọ miiran, o jẹ titiipa kanna, nikan ni bayi okun ko sun, ṣugbọn o n wa nkan lati jẹ, ṣugbọn ko si ounjẹ. Lati yago fun idinamọ loorekoore, a yoo fi orita naa pada ti a ko ba le mu ọkan miiran.

// То же что и в 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)
    );
}

Ohun ti o ṣe pataki nipa koodu yii ni pe meji ninu mẹrin awọn ọlọgbọn gbagbe lati fi orita osi wọn silẹ. Ati pe o wa ni pe wọn jẹ ounjẹ diẹ sii, ati awọn miiran bẹrẹ lati pa ebi, botilẹjẹpe awọn okun ni pataki kanna. Nibi ti ebi ko pa wọn patapata, nitori... àwọn onímọ̀ ọgbọ́n orí burúkú máa ń gbé oríta wọn sẹ́yìn nígbà míì. O wa ni jade wipe awọn ti o dara jẹ nipa 5 igba kere ju awọn buburu eyi. Nitorina aṣiṣe kekere kan ninu koodu naa nyorisi idinku ninu iṣẹ. Nibi o tun ṣe akiyesi pe ipo toje ṣee ṣe nigbati gbogbo awọn onimọ-jinlẹ mu orita osi, ko si ọkan ọtun, wọn fi apa osi si isalẹ, duro, mu apa osi lẹẹkansi, ati bẹbẹ lọ. Ipo yii tun jẹ ebi, diẹ sii bi idinamọ laarin. Emi ko le tun ṣe. Ni isalẹ ni aworan kan fun ipo kan nibiti awọn ọlọgbọn buburu meji ti ya awọn orita mejeeji, ati pe awọn ti o dara meji ti ebi npa.

Awọn Philosophers ti o ni ifunni daradara tabi Eto NET ifigagbaga

Nibi o le rii pe awọn okun nigbakan ji dide ki o gbiyanju lati gba orisun kan. Meji ninu awọn ohun kohun mẹrin ko ṣe nkankan (aworan alawọ ewe loke).

Ikú Filosopher

Ó dára, ìṣòro kan mìíràn tí ó lè dá oúnjẹ alẹ́ ológo ti àwọn onímọ̀ ọgbọ́n orí dúró ni pé bí ọ̀kan nínú wọn bá kú lójijì pẹ̀lú àwọn àmúga ní ọwọ́ rẹ̀ (a ó sì sin ín lọ́nà yẹn). Lẹhinna awọn aladugbo yoo wa ni osi laisi ounjẹ ọsan. O le wa pẹlu koodu apẹẹrẹ fun ọran yii funrararẹ, fun apẹẹrẹ o ju silẹ NullReferenceException lẹhin ti awọn philosopher gba awọn orita. Ati pe, nipasẹ ọna, imukuro kii yoo ni ọwọ ati pe koodu pipe kii yoo mu ni lasan (fun eyi AppDomain.CurrentDomain.UnhandledException ati bẹbẹ lọ). Nitorinaa, awọn olutọju aṣiṣe nilo ni awọn okun funrararẹ ati pẹlu ifopinsi oore-ọfẹ.

Oluduro

O dara, bawo ni a ṣe yanju iṣoro yii ti awọn titiipa, ebi, ati iku? A yoo gba ọkan philosopher si awọn orita, ati awọn ti a yoo fi pelu owo iyasoto ti awọn okun fun ibi yi. Bawo ni lati ṣe? Ká sọ pé lẹ́gbẹ̀ẹ́ àwọn onímọ̀ ọgbọ́n orí kan wà tó jẹ́ adúróṣinṣin tó fún onímọ̀ ọgbọ́n orí kan láyè láti mú oríta náà. Bawo ni o yẹ ki a ṣe olutọju yii ati bi awọn ọlọgbọn yoo beere lọwọ rẹ jẹ awọn ibeere ti o wuni.

Ọna ti o rọrun julọ ni fun awọn onimọ-jinlẹ lati nirọrun nigbagbogbo beere lọwọ oluduro fun iraye si awọn orita. Awon. Bayi awọn ọlọgbọn yoo ko duro fun orita kan nitosi, ṣugbọn duro tabi beere lọwọ olutọju naa. Ni akọkọ a lo aaye Olumulo nikan fun eyi a ko lo awọn idilọwọ lati pe awọn ilana eyikeyi lati ekuro (diẹ sii lori wọn ni isalẹ).

Awọn solusan aaye olumulo

Nibi a yoo ṣe ohun kanna ti a ṣe tẹlẹ pẹlu orita kan ati awọn ọlọgbọn meji, a yoo yi ni lupu kan ati duro. Ṣugbọn nisisiyi o yoo jẹ gbogbo awọn ọlọgbọn ati, bi o ti jẹ pe, orita kanṣoṣo, i.e. a le so pe nikan ni philosopher ti o mu yi "goolu orita" lati awọn Oluduro yoo jẹ. Lati ṣe eyi a lo 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 yi ni a blocker, pẹlu, aijọju soro, kanna while(true) { if (!lock) break; }, ṣugbọn pẹlu ani diẹ "idan" ju ni SpinWait (eyi ti o ti lo nibẹ). Ní báyìí, ó mọ bó ṣe lè ka àwọn tó ń dúró dè, kó mú wọn sùn díẹ̀, àti púpọ̀ sí i. bbl Ni gbogbogbo, o ṣe ohun gbogbo ti ṣee ṣe lati mu. Ṣugbọn a gbọdọ ranti pe eyi tun jẹ lupu ti nṣiṣe lọwọ kanna ti o jẹ awọn orisun ero isise ati mu o tẹle ara kan, eyiti o le ja si ebi ti ọkan ninu awọn onimọ-jinlẹ di pataki ju awọn miiran lọ, ṣugbọn ko ni orita goolu (iṣoro Inversion pataki). ). Nitorinaa, a lo nikan fun awọn ayipada kukuru pupọ ni iranti pinpin, laisi awọn ipe ẹnikẹta eyikeyi, awọn titiipa itẹ-ẹiyẹ, tabi awọn iyanilẹnu miiran.

Awọn Philosophers ti o ni ifunni daradara tabi Eto NET ifigagbaga

Yiya fun SpinLock. Awọn ṣiṣan n “ija” nigbagbogbo fun orita goolu naa. Awọn ikuna waye - agbegbe ti o ṣe afihan ni nọmba. Awọn ohun kohun ko ni lilo ni kikun: nikan nipa 2/3 nipasẹ awọn okun mẹrin wọnyi.

Ojutu miiran nibi yoo jẹ lilo nikan Interlocked.CompareExchange pẹlu iduro ti nṣiṣe lọwọ kanna bi o ti han ninu koodu loke (ninu awọn onimọ-jinlẹ ti ebi npa), ṣugbọn eyi, bi a ti sọ tẹlẹ, le ni imọ-jinlẹ ja si idinamọ.

on Interlocked o tọ lati sọ pe kii ṣe nikan CompareExchange, sugbon tun awọn ọna miiran fun atomiki kika ATI kikọ. Ati nipa atunwi iyipada, ti o tẹle ara miiran ba ṣakoso lati ṣe awọn ayipada rẹ (ka 1, ka 2, kọ 2, kọ 1 ko dara), o le ṣee lo fun awọn iyipada eka si iye kan (Interlocked Anything pattern).

Ekuro Ipo Solusan

Lati yago fun sisọnu awọn orisun ni lupu, jẹ ki a wo bii o ṣe le di okun waya kan. Ni awọn ọrọ miiran, tẹsiwaju apẹẹrẹ wa, jẹ ki a wo bi olutọju naa ṣe mu ọlọgbọn naa sùn ti o si ji i nikan nigbati o jẹ dandan. Ni akọkọ, jẹ ki a wo bii a ṣe le ṣe eyi nipasẹ ipo ekuro ti ẹrọ iṣẹ. Gbogbo awọn ẹya ti o wa nibẹ nigbagbogbo pari ni jijẹ ju awọn ti o wa ni aaye olumulo lọ. Losokepupo ni igba pupọ, fun apẹẹrẹ AutoResetEvent boya 53 igba losokepupo SpinLock [Richter]. Ṣugbọn pẹlu iranlọwọ wọn, o le muuṣiṣẹpọ awọn ilana kọja gbogbo eto, iṣakoso tabi rara.

Apẹrẹ ipilẹ nibi jẹ semaphore kan, ti a dabaa nipasẹ Dijkstra diẹ sii ju idaji orundun kan sẹhin. Semaphore jẹ, ni irọrun fi sii, odidi rere ti o ṣakoso nipasẹ eto, ati awọn iṣẹ meji lori rẹ - alekun ati dinku. Ti ko ba ṣee ṣe lati dinku odo, lẹhinna okun pipe ti dina. Nigbati nọmba naa ba pọ sii nipasẹ okun miiran / ilana ti nṣiṣe lọwọ, lẹhinna awọn okun naa ti kọja ati pe semaphore tun dinku nipasẹ nọmba ti o kọja. O le fojuinu awọn ọkọ oju irin ni igo kan pẹlu semaphore kan. NET nfunni ni ọpọlọpọ awọn itumọ pẹlu iṣẹ ṣiṣe kanna: AutoResetEvent, ManualResetEvent, Mutex ati awọn ara mi Semaphore. A yoo lo AutoResetEventEyi ni o rọrun julọ ti awọn itumọ wọnyi: awọn iye meji nikan 0 ati 1 (eke, otitọ). Ọna rẹ WaitOne() ohun amorindun awọn okun o tẹle ti o ba ti iye je 0, ati ti o ba 1, ki o si demotes o si 0 ati skips o. Ọna kan Set() pọ si 1 ati ki o jẹ ki ọkan eniyan nipasẹ, ti o lẹẹkansi dinku si 0. Ṣiṣẹ bi a turnstile ninu awọn alaja.

Jẹ ki ká complicate awọn ojutu ati ki o lo ìdènà fun kọọkan philosopher, ati ki o ko fun gbogbo ni ẹẹkan. Awon. Bayi ọpọlọpọ awọn ọlọgbọn le jẹun ni ẹẹkan, kii ṣe ọkan nikan. Ṣugbọn a tun ṣe idiwọ iwọle si tabili lati le mu awọn orita ni deede, yago fun awọn ipo ije.

// Для блокирования отдельного философа.
// Инициализируется: 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();
}

Lati loye ohun ti n ṣẹlẹ nibi, ṣe akiyesi ọran naa nigbati ọlọgbọn naa kuna lati mu awọn orita, lẹhinna awọn iṣe rẹ yoo jẹ bi atẹle. O nduro fun wiwọle si tabili. Lehin ti o ti gba, o gbiyanju lati mu awọn orita. Ko ṣiṣẹ jade. O funni ni iwọle si tabili (iyasọtọ kọọkan). Ó sì kọjá “ìyípo” rẹ̀ (AutoResetEvent) (ni akọkọ wọn wa ni sisi). O ṣubu sinu ọmọ lẹẹkansi, nitori ko ni orita. O gbiyanju lati mu wọn o si duro ni "turnstile" rẹ. Diẹ ninu awọn aladuugbo ti o ni orire ni apa ọtun tabi osi, ti wọn ti jẹun tan, yoo ṣii ni idinamọ ọlọgbọn wa nipa “ṣii ipadabọ rẹ.” Wa philosopher lọ nipasẹ o (ati awọn ti o tilekun lẹhin rẹ) a keji akoko. Gbiyanju fun igba kẹta lati mu awọn orita. Aseyori. Ati awọn ti o lọ nipasẹ rẹ turnstile lati jẹ ọsan.

Nigbati awọn aṣiṣe laileto ba wa ninu iru koodu (wọn nigbagbogbo wa), fun apẹẹrẹ, aladugbo yoo sọ ni aṣiṣe tabi ohun kanna yoo ṣẹda. AutoResetEvent fun gbogbo (Enumerable.Repeat), lẹhinna awọn ọlọgbọn yoo duro fun awọn olupilẹṣẹ, nitori Wiwa awọn aṣiṣe ni iru koodu jẹ iṣẹ ti o nira pupọ. Iṣoro miiran pẹlu ojutu yii ni pe ko ṣe idaniloju pe diẹ ninu awọn onimọ-jinlẹ kii yoo pa ebi.

Awọn ojutu arabara

A wo awọn ọna meji si mimuuṣiṣẹpọ, nigba ti a ba duro ni ipo olumulo ati yiyi ni lupu kan ati nigba ti a ba di o tẹle okun nipasẹ ekuro. Ọna akọkọ jẹ dara fun awọn bulọọki kukuru, keji fun awọn pipẹ. Nigbagbogbo o jẹ dandan lati kọkọ duro ni ṣoki fun oniyipada kan lati yipada ni lupu kan, lẹhinna di okùn okun nigbati idaduro ba gun. Ilana yii jẹ imuse ni ohun ti a npe ni. arabara awọn aṣa. O ni awọn itumọ kanna bi fun ipo kernel, ṣugbọn ni bayi pẹlu lupu ipo olumulo kan: SemaphorSlim, ManualResetEventSlim bbl Awọn julọ gbajumo oniru nibi ni Monitor, nitori ni C # ti wa ni a daradara-mọ lock sintasi. Monitor Eleyi jẹ kanna semaphore pẹlu kan ti o pọju iye ti 1 (mutex), ṣugbọn pẹlu support fun nduro ni a lupu, recursion, Ipò Àyípadà Àpẹẹrẹ (diẹ sii lori wipe isalẹ), bbl Jẹ ki a wo ojutu kan pẹlu rẹ.

// Спрячем объект для Монитора от всех, чтобы без дедлоков.
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);
    }
}

Nibi a tun ṣe idiwọ gbogbo tabili lati wọle si awọn orita, ṣugbọn ni bayi a ṣii gbogbo awọn okun ni ẹẹkan, dipo awọn aladugbo nigbati ẹnikan ba jẹun. Awon. Lákọ̀ọ́kọ́, ẹnì kan ń jẹ, ó sì dí àwọn aládùúgbò rẹ̀ lọ́wọ́, nígbà tí ẹnì kan bá sì parí rẹ̀, ṣùgbọ́n tí ó fẹ́ tún jẹun lẹ́sẹ̀kẹsẹ̀, ó lọ sínú pápá náà ó sì jí àwọn aládùúgbò rẹ̀, nítorí pé. akoko idaduro rẹ kere.

Ni ọna yii a yago fun awọn titiipa ati ebi ti awọn ọlọgbọn kan. A lo lupu kan lati duro fun igba diẹ ati dina okun fun igba pipẹ. Ṣii silẹ gbogbo eniyan ni ẹẹkan jẹ o lọra ju ti o ba jẹ pe aladugbo nikan ni ṣiṣi silẹ, bi ninu ojutu pẹlu AutoResetEvent, ṣugbọn iyatọ ko yẹ ki o tobi, nitori awọn okun gbọdọ wa ni ipo olumulo ni akọkọ.

У lock sintasi ni o ni diẹ ninu awọn unpleasant awọn iyanilẹnu. Ti ṣe iṣeduro lati lo Monitor taara [Richter] [Eric Lippert]. Ọkan ninu wọn ni pe lock nigbagbogbo ba jade Monitor, Paapa ti o ba jẹ iyasọtọ, lẹhinna o tẹle ara miiran le yi ipo iranti ti a pin pada. Ni iru awọn ọran, o dara nigbagbogbo lati lọ sinu titiipa tabi bakan lailewu fopin si eto naa. Iyalẹnu miiran ni pe Atẹle nlo awọn bulọọki aago (SyncBlock), eyi ti o wa ni gbogbo awọn ohun elo. Nitorinaa, ti o ba yan nkan ti ko yẹ, o le ni irọrun gba titiipa kan (fun apẹẹrẹ, ti o ba tii lori okun ti a fi sii). Nigbagbogbo a lo ohun ti o farapamọ fun eyi.

Apẹrẹ Iyipada Ipò n gba ọ laaye lati ṣe ni ṣoki diẹ sii ni ireti ti diẹ ninu ipo eka. Ni .NET o jẹ pe, ni ero mi, nitori ... Ni imọran, awọn ila pupọ yẹ ki o wa lori ọpọlọpọ awọn oniyipada (bii ninu Posix Threads), kii ṣe lori titiipa kan. Lẹhinna o yoo ṣee ṣe lati ṣe wọn fun gbogbo awọn onimọ-jinlẹ. Ṣugbọn paapaa ni fọọmu yii o gba ọ laaye lati kuru koodu naa.

Ọpọlọpọ awọn philosophers tabi async / await

O dara, ni bayi a le dena awọn okun. Ṣùgbọ́n bí a bá ní ọ̀pọ̀ onímọ̀ ọgbọ́n orí ńkọ́? 100? 10000? Fun apẹẹrẹ, a gba awọn ibeere 100000 si olupin wẹẹbu naa. Ṣiṣẹda okun fun ibeere kọọkan yoo jẹ gbowolori, nitori ki ọpọlọpọ awọn okun yoo wa ko le ṣe ni afiwe. Nikan bi ọpọlọpọ awọn mogbonwa ohun kohun yoo wa ni executed (Mo ni 4). Ati pe gbogbo eniyan yoo gba awọn orisun nirọrun. Ojutu kan si iṣoro yii ni apẹrẹ async / duro. Ero rẹ ni pe iṣẹ kan ko di o tẹle ara ti o ba nilo lati duro fun ohun kan lati tẹsiwaju. Ati nigbati nkan ba ṣẹlẹ, o tun bẹrẹ ipaniyan rẹ (ṣugbọn kii ṣe dandan ni okun kanna!). Ninu ọran wa, a yoo duro fun orita kan.

SemaphoreSlim ni fun eyi WaitAsync() ọna. Eyi jẹ imuse nipa lilo apẹrẹ yii.

// Запуск такой же, как раньше. Где-нибудь в программе:
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();
}

Ọna pẹlu async / await ti wa ni túmọ sinu a cunning adópin ipinle ẹrọ, eyi ti lẹsẹkẹsẹ pada awọn oniwe-ti abẹnu Task. Nipasẹ rẹ, o le duro fun ọna lati pari, fagilee, ati ohun gbogbo miiran ti o le ṣe pẹlu Iṣẹ-ṣiṣe. Ninu ọna naa, ẹrọ ipinlẹ kan n ṣakoso ipaniyan. Laini isalẹ ni pe ti ko ba si idaduro, lẹhinna ipaniyan jẹ amuṣiṣẹpọ, ati pe ti o ba wa, lẹhinna okun naa ti tu silẹ. Fun oye ti o dara julọ ti eyi, o dara lati wo ẹrọ ipinlẹ yii. O le ṣẹda awọn ẹwọn lati awọn wọnyi async / await awọn ọna.

Jẹ ki a danwo. Iṣẹ ti awọn onimọ-jinlẹ 100 lori ẹrọ kan pẹlu awọn ohun kohun ọgbọn 4, awọn aaya 8. Ojutu iṣaaju pẹlu Atẹle nikan ṣe awọn okun 4 akọkọ ati pe ko ṣiṣẹ iyokù rara. Ọkọọkan ninu awọn okun mẹrin wọnyi jẹ aiṣiṣẹ fun bii 4ms. Ati ojutu async / await ṣe gbogbo 2, pẹlu aropin 100 awọn aaya kọọkan duro. Nitoribẹẹ, ni awọn eto gidi, aiṣiṣẹ fun awọn aaya 6.8 jẹ itẹwẹgba ati pe o dara julọ lati ma ṣe ilana ọpọlọpọ awọn ibeere ni ọna yii. Ojutu pẹlu Atẹle yipada lati ko ni iwọn rara.

ipari

Gẹgẹbi o ti le rii lati awọn apẹẹrẹ kekere wọnyi, .NET ṣe atilẹyin ọpọlọpọ awọn imuṣiṣẹpọ amuṣiṣẹpọ. Sibẹsibẹ, kii ṣe nigbagbogbo han bi o ṣe le lo wọn. Mo nireti pe nkan yii ṣe iranlọwọ. A n murasilẹ eyi fun bayi, ṣugbọn ọpọlọpọ awọn nkan ti o nifẹ si tun wa, fun apẹẹrẹ, awọn ikojọpọ okun-ailewu, TPL Dataflow, siseto Reactive, Awoṣe Iṣowo sọfitiwia, ati bẹbẹ lọ.

Awọn orisun

orisun: www.habr.com

Fi ọrọìwòye kun