Filozòf ki byen manje oswa pwogramasyon .NET konpetitif

Filozòf ki byen manje oswa pwogramasyon .NET konpetitif

Ann gade kouman pwogramasyon konkouran ak paralèl travay nan .Net, lè l sèvi avèk egzanp pwoblèm nan filozòf manje midi. Plan an se jan sa a, soti nan senkronizasyon fil/pwosesis nan modèl la aktè (nan pati sa yo). Atik la ka itil pou yon premye konesans oswa pou rafrechi konesans ou.

Poukisa menm konnen ki jan fè sa? Tranzistò yo rive nan gwosè minimòm yo, lwa Moore a frape limit la nan vitès limyè a, ak Se poutèt sa kwasans obsève nan nimewo plis tranzistò yo ka fè. An menm tan an, kantite done ap grandi, ak itilizatè yo atann repons imedya nan men sistèm yo. Nan yon sitiyasyon konsa, pwogramasyon "nòmal", lè nou gen yon sèl fil egzekite, pa efikas ankò. Nou bezwen yon jan kanmenm rezoud pwoblèm nan nan ekzekisyon similtane oswa konkouran. Anplis, pwoblèm sa a egziste nan diferan nivo: nan nivo fil, nan nivo pwosesis, nan nivo machin sou rezo a (sistèm distribiye). .NET gen bon jan kalite teknoloji ki teste tan pou rezoud pwoblèm sa yo byen vit ak efikasite.

Objektif Travay la

Edsger Dijkstra te mande etidyan li yo pwoblèm sa a an 1965. Fòmilasyon etabli a se jan sa a. Gen yon sèten (anjeneral senk) kantite filozòf ak menm kantite fouchèt. Yo chita sou yon tab wonn, fouchèt ant yo. Filozòf yo ka manje nan asyèt yo nan manje kontinuèl, panse oswa rete tann. Pou manje, yon filozòf bezwen pran de fouchèt (dènye a pataje yon fouchèt ak ansyen an). Ranmase ak depoze yon fouchèt se de aksyon separe. Tout filozòf yo silans. Travay la se jwenn yon algorithm konsa pou yo tout panse epi yo byen manje menm apre 54 ane.

Premyèman, ann eseye rezoud pwoblèm sa a lè nou itilize espas pataje. Fouchèt yo kouche sou tab komen an epi filozòf yo tou senpleman pran yo lè yo la epi mete yo tounen. Sa a se kote pwoblèm ak senkronizasyon leve, lè egzakteman yo pran fouchèt? kisa ou dwe fè si pa gen okenn ploge? elatriye Men, anvan, ann kòmanse ak filozòf yo.

Pou kòmanse fil nou itilize yon pisin fil via Task.Run metòd:

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

Pisin fil la fèt pou optimize kreyasyon ak retire fil yo. Pisin sa a gen yon keu nan travay ak CLR a kreye oswa efase fil depann sou kantite travay sa yo. Yon pisin pou tout AppDomains. Pisin sa a ta dwe itilize prèske tout tan, paske... pa bezwen deranje ak kreye ak efase fil, ke moun kap kriye yo, elatriye Ou ka fè li san yon pisin, men Lè sa a, ou pral oblije sèvi ak li dirèkteman. Thread, sa a se itil pou ka lè nou bezwen chanje priyorite nan yon fil, lè nou gen yon operasyon long, pou yon fil Premye plan, elatriye.

Nan yon lòt sans, System.Threading.Tasks.Task klas se menm bagay la Thread, men ak tout kalite konvenyans: kapasite nan lanse yon travay apre yon blòk nan lòt travay, retounen yo soti nan fonksyon, fasilman entèwonp yo, ak plis ankò. elatriye Yo bezwen pou sipòte async/wait konstriksyon (Task ki baze sou Asynchrone Pattern, sik sentaktik pou tann pou operasyon IO). Nou pral pale sou sa pita.

CancelationTokenSource isit la li nesesè ke fil la ka mete fen nan tèt li sou yon siyal soti nan fil la rele.

Pwoblèm Sync

Filozòf bloke

Oke, nou konnen ki jan yo kreye fil, ann eseye manje midi:

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

Isit la nou premye eseye pran fouchèt gòch la ak Lè sa a, dwat la, epi si li travay, nou manje epi mete yo tounen. Pran yon sèl fouchèt se atomik, i.e. de fil pa ka pran youn an menm tan (mal: premye a li ke fouchèt la gratis, dezyèm nan fè menm bagay la, premye a pran, dezyèm lan pran). Pou sa Interlocked.CompareExchange, ki dwe aplike lè l sèvi avèk yon enstriksyon processeur (TSL, XCHG), ki fèmen yon moso memwa pou lekti ak ekri sekans atomik. Ak SpinWait ekivalan a konstriksyon an while(true) sèlman ak yon ti kras "majik" - fil la pran processeur a (Thread.SpinWait), men pafwa pase kontwòl nan yon lòt fil (Thread.Yeild) oswa tonbe nan dòmi (Thread.Sleep).

Men, solisyon sa a pa mache, paske... fil yo talè (nan yon segond pou mwen) yo bloke: tout filozòf yo pran fouchèt gòch yo, men pa gen youn dwa. Lè sa a, etalaj fouchèt la gen valè yo: 1 2 3 4 5.

Filozòf ki byen manje oswa pwogramasyon .NET konpetitif

Nan foto a, bloke fil (enpas). Vèt endike ekzekisyon, wouj endike senkronizasyon, ak gri endike fil la ap dòmi. Diamonds endike tan lansman travay yo.

Grangou filozòf yo

Malgre ke ou pa bezwen anpil manje pou panse, grangou ka fòse nenpòt moun ki abandone filozofi. Se pou nou eseye simulation sitiyasyon an nan grangou fil nan pwoblèm nou an. Grangou se lè yon fil travay, men san travay enpòtan, nan lòt mo, li nan menm enpas la, se sèlman kounye a fil la pa dòmi, men se aktivman kap chèche yon bagay yo manje, men pa gen okenn manje. Pou evite bloke souvan, nou pral mete fouchèt la tounen si nou pa t 'kapab pran yon lòt.

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

Sa ki enpòtan nan kòd sa a se ke de sou kat filozòf bliye mete fouchèt gòch yo. Epi li sanble ke yo manje plis manje, ak lòt moun kòmanse mouri grangou, byenke fil yo gen menm priyorite. Isit la yo pa mouri grangou nèt, paske... move filozòf pafwa mete fouchèt yo tounen. Li sanble ke bon yo manje apeprè 5 fwa mwens pase move yo. Se konsa, yon ti erè nan kòd la mennen nan yon gout nan pèfòmans. La a tou se yon valè de note ke yon sitiyasyon ki ra posib lè tout filozòf pran fouchèt gòch la, pa gen dwa, yo mete gòch la desann, tann, pran youn nan gòch la ankò, elatriye. Sitiyasyon sa a se tou grangou, plis tankou yon blokaj mityèl. Mwen pa t 'kapab repete li. Anba a se yon foto pou yon sitiyasyon kote de move filozòf te pran tou de fouchèt, ak de bon yo ap mouri grangou.

Filozòf ki byen manje oswa pwogramasyon .NET konpetitif

Isit la ou ka wè ke fil pafwa reveye epi eseye jwenn yon resous. De sou kat nwayo pa fè anyen (graf vèt pi wo a).

Lanmò yon filozòf

Oke, yon lòt pwoblèm ki ta ka entèwonp dine nan bèl pouvwa nan filozòf se si youn nan yo toudenkou mouri ak fouchèt nan men l '(epi li pral antere l konsa). Lè sa a, vwazen yo pral rete san manje midi. Ou ka vini ak yon kòd egzanp pou ka sa a tèt ou, pou egzanp li jete NullReferenceException apre filozòf la pran fouchèt yo. Epi, nan chemen an, eksepsyon an pa pral okipe epi kòd apèl la pa pral tou senpleman trape li (pou sa a AppDomain.CurrentDomain.UnhandledException ak elatriye). Se poutèt sa, moun ki okipe erè yo bezwen nan fil yo tèt yo ak ak revokasyon grasyeuz.

Gason an

Oke, ki jan nou rezoud pwoblèm sa a nan enpas, grangou, ak lanmò? Nou pral pèmèt sèlman yon sèl filozòf nan fouchèt yo, epi nou pral ajoute eksklizyon mityèl nan fil pou kote sa a. Ki jan fè li? Sipoze akote filozòf yo gen yon gason ki bay pèmisyon yon sèl filozòf pou pran fouchèt yo. Ki jan nou ta dwe fè gason sa a ak ki jan filozòf yo pral mande l 'yo se kesyon ki enteresan.

Fason ki pi senp la se pou filozòf yo tou senpleman mande gason an pou jwenn aksè nan fouchèt yo. Moun sa yo. Koulye a, filozòf pa pral tann pou yon fouchèt ki tou pre, men rete tann oswa mande gason an. Okòmansman, nou itilize sèlman Espas Itilizatè pou sa a, nou pa sèvi ak entèwonp pou rele nenpòt pwosedi ki soti nan nwayo a (plis sou yo anba a).

Solisyon espas itilizatè yo

Isit la nou pral fè menm bagay nou te fè anvan ak yon sèl fouchèt ak de filozòf, nou pral vire nan yon bouk epi tann. Men koulye a, li pral tout filozòf ak, kòm li te, yon sèl fouchèt, i.e. nou ka di ke se sèlman filozòf la ki te pran "fouchèt an lò" sa a nan men gason an ap manje. Pou fè sa nou itilize 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 sa a se yon bloke, ak, apeprè pale, menm bagay la while(true) { if (!lock) break; }, men ak menm plis "majik" pase nan SpinWait (ki itilize la). Kounye a li konnen ki jan yo konte moun k ap tann, mete yo dòmi yon ti kras, ak plis ankò. elatriye An jeneral, li fè tout sa ki posib pou optimize. Men, nou dwe sonje ke sa a se toujou menm bouk aktif ki manje resous processeur ak kenbe yon fil, ki ka mennen nan grangou si youn nan filozòf yo vin pi priyorite pase lòt yo, men li pa gen yon fouchèt an lò (pwoblèm priyorite Envèsyon ). Se poutèt sa, nou itilize li sèlman pou chanjman trè trè kout nan memwa pataje, san okenn apèl twazyèm pati, kadna enbrike, oswa lòt sipriz.

Filozòf ki byen manje oswa pwogramasyon .NET konpetitif

Desen pou SpinLock. Rivyè yo toujou ap "batay" pou fouchèt an lò. Echèk rive - zòn nan make nan figi a. Nwayo yo pa konplètman itilize: sèlman apeprè 2/3 pa kat fil sa yo.

Yon lòt solisyon isit la ta dwe itilize sèlman Interlocked.CompareExchange ak menm tann aktif jan yo montre nan kòd ki pi wo a (nan filozòf ki mouri grangou), men sa a, kòm deja di, teyorikman ta ka mennen nan bloke.

sou Interlocked li vo di ke pa gen sèlman CompareExchange, men tou lòt metòd pou lekti atomik AK ekri. Epi lè w repete chanjman an, si yon lòt fil jere fè chanjman li yo (li 1, li 2, ekri 2, ekri 1 se move), li ka itilize pou chanjman konplèks nan yon valè (modèl Interlocked Anything).

Solisyon mòd Kernel

Pou evite gaspiye resous nan yon bouk, ann gade nan ki jan yo bloke yon fil. Nan lòt mo, kontinye egzanp nou an, ann wè ki jan gason an mete filozòf la nan dòmi epi reveye li sèlman lè sa nesesè. Premyèman, ann gade kijan pou fè sa atravè mòd nwayo sistèm operasyon an. Tout estrikti gen souvan fini pi dousman pase sa yo ki nan espas itilizatè. Pi dousman plizyè fwa, pou egzanp AutoResetEvent petèt 53 fwa pi dousman SpinLock [Richter]. Men, avèk èd yo, ou ka senkronize pwosesis atravè tout sistèm nan, jere oswa ou pa.

Konsepsyon debaz la isit la se yon semafò, pwopoze pa Dijkstra plis pase mwatye yon syèk de sa. Yon semafò se, tou senpleman mete, yon nonb antye relatif pozitif kontwole pa sistèm nan, ak de operasyon sou li - ogmante ak diminye. Si li pa posib diminye zewo, Lè sa a, fil apèl la bloke. Lè nimewo a ogmante pa kèk lòt fil aktif/pwosesis, Lè sa a, fil yo pase epi semafò a ankò diminye pa nimewo a pase. Ou ka imajine tren nan yon kou boutèy ak yon semafò. .NET ofri plizyè konstriksyon ak fonksyonalite menm jan an: AutoResetEvent, ManualResetEvent, Mutex ak tèt mwen Semaphore. Nou pral itilize AutoResetEvent, sa a se pi senp nan konstriksyon sa yo: sèlman de valè 0 ak 1 (fo, vre). Metòd li WaitOne() bloke fil apèl la si valè a te 0, epi si 1, Lè sa a, degrade li a 0 epi sote li. Yon metòd Set() ogmante a 1 epi kite yon moun pase, ki ankò diminye a 0. Aji tankou yon tournike nan tren an.

Ann konplike solisyon an epi sèvi ak bloke pou chak filozòf, epi pa pou tout an menm tan. Moun sa yo. Koulye a, plizyè filozòf ka manje nan yon fwa, epi yo pa sèlman youn. Men, nou ankò bloke aksè nan tab la yo nan lòd yo kòrèkteman pran fouchèt, evite kondisyon ras.

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

Pou konprann sa k ap pase isit la, konsidere ka a lè filozòf la echwe pou pou pran fouchèt yo, Lè sa a, aksyon li yo pral jan sa a. Li ap tann aksè nan tab la. Lè li resevwa li, li eseye pran fouchèt yo. Pa t 'travay deyò. Li bay aksè nan tab la (esklizyon mityèl). Epi li pase "tournik" li a (AutoResetEvent) (nan premye yo louvri). Li tonbe nan sik la ankò, paske li pa gen fouchèt. Li eseye pran yo epi li kanpe nan "tournike" li. Gen kèk vwazen ki gen plis chans sou bò dwat oswa sou goch, lè li fin manje, k ap debloke filozòf nou an nan “louvri tournike l”. Filozòf nou an ale nan li (epi li fèmen dèyè l ') yon dezyèm fwa. Eseye pou twazyèm fwa a pran fouchèt yo. Siksè. Apre sa, li ale nan tournike l 'yo manje midi.

Lè gen erè o aza nan kòd sa a (yo toujou egziste), pou egzanp, yo pral espesifye yon vwazen mal oswa menm objè a pral kreye. AutoResetEvent pou tout (Enumerable.Repeat), Lè sa a, filozòf yo ap tann pou devlopè yo, paske Jwenn erè nan kòd sa yo se byen yon travay difisil. Yon lòt pwoblèm ak solisyon sa a se ke li pa garanti ke kèk filozòf pa pral mouri grangou.

Solisyon ibrid

Nou te gade de apwòch nan senkronizasyon, lè nou rete nan mòd itilizatè ak vire nan yon bouk ak lè nou bloke fil la nan nwayo a. Premye metòd la bon pou blòk kout, dezyèm lan pou blòk ki long. Souvan ou bezwen premye tann yon ti tan pou yon varyab chanje nan yon bouk, ak Lè sa a, bloke fil la lè tann lan long. Apwòch sa a se aplike nan sa yo rele. desen ibrid. Li gen menm konstriksyon ak mòd nwayo, men kounye a ak yon bouk mòd itilizatè: SemaphorSlim, ManualResetEventSlim elatriye konsepsyon ki pi popilè isit la se Monitor, paske nan C# gen yon byen li te ye lock sentaks. Monitor sa a se menm semafò a ak yon valè maksimòm 1 (mutex), men ak sipò pou ap tann nan yon bouk, rekursion, kondisyon varyab modèl (plis sou sa ki anba a), elatriye Ann gade nan yon solisyon ak li.

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

Isit la nou ankò bloke tab la tout antye nan aksè nan fouchèt yo, men kounye a nou debloke tout fil nan yon fwa, olye ke vwazen lè yon moun fini manje. Moun sa yo. premyèman, yon moun manje epi bloke vwazen yo, epi lè sa a yon moun fini, men li vle manje ankò touswit, li antre nan blòk la li reveye vwazen li yo, paske tan ap tann li se mwens.

Nan fason sa a nou evite enpas ak grangou nan kèk filozòf. Nou itilize yon bouk pou tann pou yon ti tan epi bloke fil la pou yon tan long. Debloke tout moun nan yon fwa se pi dousman pase si sèlman vwazen an te debloke, tankou nan solisyon an ak AutoResetEvent, men diferans lan pa ta dwe gwo, paske fil yo dwe rete nan mòd itilizatè an premye.

У lock sentaks gen kèk sipriz dezagreyab. Rekòmande pou itilize Monitor dirèkteman [Richter] [Eric Lippert]. Youn nan yo se sa lock toujou soti Monitor, menm si te gen yon eksepsyon, ak Lè sa a, yon lòt fil ka chanje eta a nan memwa a pataje. Nan ka sa yo, li souvan pi bon ale nan yon enpas oswa yon jan kanmenm san danje mete fen nan pwogram nan. Yon lòt sipriz se ke Monitor itilize blòk revèy (SyncBlock), ki prezan nan tout objè yo. Se poutèt sa, si yo chwazi yon objè ki pa apwopriye, ou ka fasilman jwenn yon enpas (pa egzanp, si ou fèmen sou yon fisèl entène). Nou toujou sèvi ak yon objè kache pou sa.

Modèl Kondisyon Varyab la pèmèt ou aplike plis egzijans atant nan kèk kondisyon konplèks. Nan .NET li enkonplè, nan opinyon mwen, paske... Nan teyori, ta dwe gen plizyè ke moun kap kriye sou plizyè varyab (tankou nan fil Posix), epi yo pa sou yon sèl fèmen. Lè sa a, li ta posib fè yo pou tout filozòf. Men, menm nan fòm sa a li pèmèt ou vin pi kout kòd la.

Anpil filozòf oswa async / await

Oke, kounye a nou ka efektivman bloke fil. Men, e si nou gen anpil filozòf? 100? 10000? Pou egzanp, nou te resevwa 100000 demann nan sèvè entènèt la. Kreye yon fil pou chak demann yo pral chè, paske anpil fil pa pral egzekite an paralèl. Se sèlman kòm anpil nwayo lojik pral egzekite (mwen gen 4). Ak tout lòt moun pral tou senpleman retire resous yo. Yon solisyon a pwoblèm sa a se modèl async / tann. Lide li se ke yon fonksyon pa kenbe yon fil si li bezwen rete tann pou yon bagay kontinye. Men, lè yon bagay rive, li rekòmanse ekzekisyon li (men pa nesesèman nan menm fil la!). Nan ka nou an, nou pral tann pou yon fouchèt.

SemaphoreSlim gen pou sa WaitAsync() metòd. Isit la se yon aplikasyon ki itilize modèl sa a.

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

Metòd ak async / await se tradwi nan yon machin atizan fini fini, ki imedyatman retounen entèn li yo Task. Atravè li, ou ka tann pou metòd la fini, anile li, ak tout lòt bagay ou ka fè ak Task. Anndan metòd la, yon machin leta kontwole ekzekisyon. Liy anba a se ke si pa gen okenn reta, Lè sa a, ekzekisyon an se synchrone, epi si gen, Lè sa a, fil la lage. Pou yon pi bon konpreyansyon sa a, li se pi bon gade nan machin leta sa a. Ou ka kreye chenn nan sa yo async / await metòd.

Ann teste li. Travay 100 filozòf sou yon machin ki gen 4 nwayo lojik, 8 segonn. Solisyon anvan an ak Monitè sèlman egzekite premye 4 fil yo epi yo pa t 'ekzekisyon rès la nan tout. Chak nan 4 fil sa yo te san fè anyen konsa pou apeprè 2ms. Ak solisyon an async / tann te fè tout 100, ak yon mwayèn de 6.8 segonn chak tann. Natirèlman, nan sistèm reyèl, yo te san fè anyen konsa pou 6 segonn se akseptab epi li pi bon pa trete anpil demann nan fason sa a. Solisyon an ak monitè kè bebe yo te tounen soti yo dwe pa évolutive ditou.

Konklizyon

Kòm ou ka wè nan ti egzanp sa yo, .NET sipòte anpil konstriksyon senkronizasyon. Sepandan, li pa toujou evidan ki jan yo sèvi ak yo. Mwen espere atik sa a te itil. Nou ap vlope sa a pou kounye a, men gen toujou yon anpil nan bagay enteresan ki rete, pou egzanp, koleksyon fil ki san danje, TPL Dataflow, pwogram reyaktif, modèl tranzaksyon lojisyèl, elatriye.

Sous

Sous: www.habr.com

Add nouvo kòmantè