ʻO nā Philosophers i hānai maikaʻi ʻia a i ʻole ka hoʻokūkū hoʻokūkū .NET Programming

ʻO nā Philosophers i hānai maikaʻi ʻia a i ʻole ka hoʻokūkū hoʻokūkū .NET Programming

E ʻike kākou i ka hana like ʻana a me ka hoʻolālā like ʻana ma .Net, me ka hoʻohana ʻana i ka Philosophers Dining Problem i laʻana. ʻO kēia ka hoʻolālā, mai ka hoʻonohonoho ʻana o nā kaula / kaʻina hana, i ke kumu hoʻohālike (ma nā ʻāpana aʻe). Pono paha ka ʻatikala no ka ʻike mua a i ʻole e hōʻoluʻolu i kou ʻike.

No ke aha e hana ai? Hiki i nā transistors i ko lākou liʻiliʻi liʻiliʻi, ke kau nei ke kānāwai o Moore ma ka palena o ka māmā o ka māmā a no laila ʻike ʻia ka piʻi ʻana o ka helu, hiki ke hana i nā transistors. I ka manawa like, ke ulu nei ka nui o ka ʻikepili, a manaʻo nā mea hoʻohana i ka pane koke mai nā ʻōnaehana. Ma ia ʻano, ʻaʻole pono ka polokalamu "maʻamau", ke loaʻa iā mākou hoʻokahi pae hoʻokō. Pono ʻoe e hoʻoponopono i ka pilikia o ka hoʻokō like a i ʻole ka hoʻokō ʻana. Eia kekahi, aia kēia pilikia ma nā pae like ʻole: ma ke kiʻekiʻe o nā kaula, ma ke kiʻekiʻe o nā kaʻina hana, ma ke kiʻekiʻe o nā mīkini i loko o ka pūnaewele (nā ʻōnaehana māhele). Loaʻa iā .NET nā ʻenehana kiʻekiʻe, i hoʻāʻo ʻia i ka manawa no ka hoʻoponopono wikiwiki ʻana i ia mau pilikia.

Nń Pahuhopu

Ua hoʻopuka ʻo Edsger Dijkstra i kēia pilikia i kāna mau haumāna i ka makahiki 1965. Penei ka hoʻokumu ʻana. Aia kekahi helu (maʻa mau ʻelima) o ka poʻe akeakamai a me ka helu like o nā ʻōpala. Noho lākou ma kahi pākaukau poepoe, nā mākia ma waena o lākou. Hiki i ka poʻe akeakamai ke ʻai i kā lākou mau papa ʻai pau ʻole, noʻonoʻo a kali paha. No ka ʻai ʻana i ka mea hoʻonaʻauao, pono ʻoe e lawe i ʻelua mau ʻōpala (ʻo ka mea hope e kaʻana like i ka ʻōpala me ka mua). ʻO ka ʻohi ʻana a me ka waiho ʻana i lalo i ka ʻōpala he ʻelua hana kaʻawale. Hamau ka poe akeakamai a pau. ʻO ka hana ka loaʻa ʻana o kahi algorithm e noʻonoʻo ai lākou āpau a piha hoʻi ma hope o 54 mau makahiki.

ʻO ka mea mua, e hoʻāʻo kākou e hoʻoponopono i kēia pilikia ma o ka hoʻohana ʻana i kahi ākea. Aia nā ʻōpuʻu ma ka pākaukau maʻamau a lawe wale ka poʻe akeakamai iā lākou i ka wā e noho ai a hoʻihoʻi. Aia nā pilikia me ka synchronization, i ka manawa e lawe ai i surebets? pehea inā ʻaʻohe ʻōpuʻu? etc. Akā ʻo ka mua, e hoʻomaka kākou i ka poʻe akeakamai.

No ka hoʻomaka ʻana i nā threads, hoʻohana mākou i kahi kolamu thread ma Task.Run ala:

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

Hoʻolālā ʻia ka kolamu thread e hoʻoikaika i ka hana ʻana a me ka holoi ʻana. Aia kēia loko i ka pila me nā hana a na ka CLR e hana a wehe paha i nā kaula ma muli o ka nui o kēia mau hana. Hoʻokahi puna no nā AppDomains āpau. Pono e hoʻohana ʻia kēia loko i nā manawa a pau, no ka mea. ʻAʻole pono e hoʻopilikia i ka hana ʻana, ka holoi ʻana i nā kaula, kā lākou queues, a me nā mea ʻē aʻe. Thread, pono kēia no nā hihia inā pono ʻoe e hoʻololi i ka mea nui o kahi thread, ke lōʻihi kā mākou hana, no kahi thread foreground, etc.

I nā huaʻōlelo ʻē aʻe, System.Threading.Tasks.Task ua like ka papa Thread, akā me nā ʻano ʻoluʻolu a pau: ka hiki ke holo i kahi hana ma hope o kahi poloka o nā hana ʻē aʻe, hoʻihoʻi iā lākou mai nā hana, hoʻopau maʻalahi iā lākou, a ʻoi aku. etc. Pono lākou e kākoʻo i nā hana async / kali (Task-based Asynchronous Pattern, syntactic sugar no ke kali ʻana i nā hana IO). E kamaʻilio mākou e pili ana i kēia ma hope.

CancelationTokenSource eia ka pono i hiki i ke kaula ke hoopau ia ia iho ma ka hoailona o ke kaula kahea.

Nā Pilikia Hoʻohui

ʻAi ʻia nā Philosophers

ʻAe, ʻike mākou i ka hana ʻana i nā pae, e hoʻāʻo kāua e ʻai i ka ʻaina awakea:

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

Maanei e hoao mua ai makou e lawe i ka lalau hema, a ma ka aoao akau, a ina pono, alaila, ai makou a hoihoi hou. ʻO ka lawe ʻana i hoʻokahi lāʻau he atomika, ʻo ia hoʻi. ʻAʻole hiki i ʻelua mau kaula ke lawe i hoʻokahi i ka manawa like (hewa: ʻo ka heluhelu mua ʻana he manuahi ka ʻōpala, ʻo ka lua - pū kekahi, lawe ka mua, lawe ka lua). No keia Interlocked.CompareExchange, pono e hoʻokō ʻia me kahi ʻōlelo aʻoaʻo (TSL, XCHG), ka mea e laka ai i kahi ʻāpana hoʻomanaʻo no ka heluhelu a me ke kākau ʻana i ke kaʻina atomika. A ua like ka SpinWait me ka hana while(true) me kahi "magic" liʻiliʻi wale nō - lawe ke kaula i ka mea hana (Thread.SpinWait), akā i kekahi manawa hoʻololi i ka mana i kekahi pae (Thread.Yeild) a hiamoe paha (Thread.Sleep).

Akā ʻaʻole pono kēia hoʻonā, no ka mea ua ālai ʻia nā kahe ʻana (noʻu i loko o ke kekona): lawe ka poʻe akeakamai a pau i ko lākou ʻaoʻao hema, ʻaʻole i ka ʻākau. A laila, loaʻa nā waiwai i ka papa ʻaina: 1 2 3 4 5.

ʻO nā Philosophers i hānai maikaʻi ʻia a i ʻole ka hoʻokūkū hoʻokūkū .NET Programming

I ke kiʻi, ke kāohi ʻana i nā kaula (deadlock). Green - hoʻokō, ʻulaʻula - synchronization, hina - ke moe nei ke kaula. Hōʻike nā rhombus i ka manawa hoʻomaka o nā hana.

Ka pololi o ka poe akeakamai

ʻOiai ʻaʻole pono ka noʻonoʻo nui ʻana i ka meaʻai, akā ʻo ka pōloli e haʻalele ai kekahi i ke akeakamai. E ho'āʻo kākou e hoʻohālike i ke kūlana o ka pōloli o nā kaula i kā mākou pilikia. ʻO ka pōloli i ka wā e holo ai kahi kaula, akā ʻaʻohe hana koʻikoʻi, ʻo ia hoʻi, ʻo ia ka mea make like, i kēia manawa ʻaʻole hiamoe ke kaula, akā ke ʻimi nei i kahi mea e ʻai ai, ʻaʻohe meaʻai. I mea e pale ʻole ai i ka pāpā pinepine ʻana, e hoʻihoʻi mākou i ka ʻōpala inā ʻaʻole hiki iā mākou ke lawe i kekahi.

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

ʻO ka mea nui e pili ana i kēia code ʻo ia ka poina ʻelua o ʻehā poʻe akeakamai i ka waiho ʻana i kā lākou ʻoki hema. A ʻike ʻia ua ʻai nui lākou i ka meaʻai, aʻo ka poʻe ʻē aʻe e hoʻomaka i ka pōloli, ʻoiai ʻo nā kaula ka mea nui. Maanei, aole lakou pololi loa, no ka mea. Hoʻihoʻi ka poʻe akamai ʻino i kā lākou mau ʻōpala i kekahi manawa. ʻIke ʻia ka ʻai ʻana o ka poʻe maikaʻi ma kahi o 5 mau manawa ma mua o nā mea ʻino. No laila, kahi hewa liʻiliʻi i ke code e alakaʻi i kahi hāʻule o ka hana. He mea kūpono hoʻi e ʻike ma aneʻi he hiki ke loaʻa kahi kūlana i ka wā e lawe ai ka poʻe akeakamai a pau ma ka ʻaoʻao hema, ʻaʻohe mea ʻākau, waiho lākou i ka hema, kali, lawe hou i ka hema, etc. He pōloli nō hoʻi kēia kūlana, ʻoi aku ka like me ka make. ʻAʻole au i hana hou. Aia ma lalo kahi kiʻi no kahi kūlana kahi i lawe ai ʻelua poʻe manaʻo maikaʻi ʻole i nā ʻōpala ʻelua a ʻelua mau mea maikaʻi e pōloli ana.

ʻO nā Philosophers i hānai maikaʻi ʻia a i ʻole ka hoʻokūkū hoʻokūkū .NET Programming

Maanei hiki iā ʻoe ke ʻike i ke ala ʻana o nā kaula i kekahi manawa a hoʻāʻo e kiʻi i ka waiwai. ʻAʻohe hana ʻelua o nā core ʻehā (ka pakuhi ʻōmaʻomaʻo ma luna).

Ka make ʻana o ke Kauka

ʻAe, ʻo kahi pilikia ʻē aʻe e hiki ke hoʻopau i kahi pāʻina nani o ka poʻe akeakamai inā e make koke kekahi o lākou me nā ʻōpala ma kona mau lima (a e kanu lākou iā ia e like me ia). A laila e waiho ʻia nā hoalauna me ka ʻole o ka ʻaina ahiahi. Hiki iā ʻoe ke hele mai me kahi code code no kēia hihia iā ʻoe iho, no ka laʻana, ua kiola ʻia NullReferenceException ma hope o ka lawe ʻana o ke akeakamai i nā ʻoki. A, ma ke ala, ʻaʻole e mālama ʻia ka ʻokoʻa a ʻaʻole e hopu wale ke code kelepona iā ia (no kēia AppDomain.CurrentDomain.UnhandledException a pela aku). No laila, pono nā mea hoʻoponopono hewa i nā kaula ponoʻī a me ka hoʻopau maikaʻi ʻana.

Waiter

ʻAe, pehea mākou e hoʻoponopono ai i kēia pilikia make, pōloli, a me ka pilikia? E ʻae mākou i hoʻokahi wale nō loea e hiki i nā ʻōpala, e hoʻohui i kahi hoʻokaʻawale ʻana o nā kaula no kēia wahi. Pehea e hana ai? Ina paha e ku ana kekahi kahu ma ka aoao o ka poe akeakamai, nana e ae aku i kekahi kanaka akeakamai e lawe i na laau. Pehea mākou e hana ai i kēia waiter a pehea e nīnau ai ka poʻe akeakamai iā ia, he mea hoihoi nā nīnau.

ʻO ke ala maʻalahi ka manawa e nīnau mau ai ka poʻe akeakamai i ka waiter no ke komo ʻana i nā ʻōpala. ʻO kēlā mau mea. i kēia manawa, ʻaʻole e kali ka poʻe akeakamai i kahi mākia kokoke, akā e kali a nīnau paha i ka waiter. I ka wā mua, hoʻohana mākou i ka Space User wale nō no kēia, ʻaʻole mākou e hoʻohana i nā interrupts e kāhea i nā kaʻina hana mai ka kernel (e pili ana iā lākou ma lalo).

Nā hāʻina ma ka wahi hoʻohana

Ma ʻaneʻi e hana like mākou e like me kā mākou hana ʻana me hoʻokahi lāʻau a me ʻelua poʻe akeakamai, e wili mākou i ka pōʻai a kali. Akā, i kēia manawa, e lilo ia i poʻe akeakamai a pau, a me he mea lā, hoʻokahi wale nō mākia, ʻo ia hoʻi. hiki ke ʻōlelo ʻia ʻo ka mea akeakamai wale nō nāna i lawe i kēia "lau gula" mai ka waiter e ʻai. No kēia, hoʻohana mākou iā 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 he mea pale keia, me ka like ole while(true) { if (!lock) break; }, akā me ka ʻoi aku o ka "magic" ma mua o ka in SpinWait (kahi i hoʻohana ʻia ma laila). I kēia manawa ʻike ʻo ia i ka helu ʻana i ka poʻe e kali ana, hoʻomoe liʻiliʻi, a ʻoi aku. etc. Ma keʻano laulā, hana i nā mea āpau e hiki ke hoʻonui. Akā, pono kākou e hoʻomanaʻo, ʻo ia nō ka pōʻaiapuni hana like e ʻai ai i nā kumuwaiwai processor a mālama i ke kahe, hiki ke alakaʻi i ka pōloli inā ʻoi aku ka nui o kekahi o ka poʻe akeakamai ma mua o nā poʻe ʻē aʻe, akā ʻaʻohe ona gula (Priority Inversion pilikia) . No laila, hoʻohana mākou iā ia no nā hoʻololi pōkole loa i ka hoʻomanaʻo like ʻana, me ka ʻole o nā kelepona ʻekolu, nā laka pūnana, a me nā mea kupanaha ʻē aʻe.

ʻO nā Philosophers i hānai maikaʻi ʻia a i ʻole ka hoʻokūkū hoʻokūkū .NET Programming

Kahakii no SpinLock. Ke "hakakā" mau nei nā kahawai no ka lāʻau gula. Aia nā hemahema - ma ke kiʻi, kahi i koho ʻia. ʻAʻole hoʻohana piha ʻia nā cores: ma kahi o 2/3 wale nō e kēia mau kaula ʻehā.

ʻO kekahi hopena ma aneʻi e hoʻohana wale Interlocked.CompareExchange me ke kali ikaika like e like me ka mea i hōʻike ʻia ma ke code ma luna (i ka poʻe philosophers pōloli), akā ʻo kēia, e like me ka mea i ʻōlelo mua ʻia, hiki ke alakaʻi i ka pale.

maluna o Interlocked Pono e hoʻomaopopoʻiaʻaʻole wale nō CompareExchange, akā, ʻo nā ʻano hana ʻē aʻe no ka heluhelu atomika A kākau. A ma o ka hoʻololi hou ʻana, inā loaʻa ka manawa e hoʻololi ai i kekahi loina (heluhelu i ka 1, heluhelu i ka 2, e kākau i ka 2, e kākau i ka 1 hewa), hiki ke hoʻohana ʻia no nā hoʻololi paʻakikī i kahi waiwai hoʻokahi (Interlocked Anything pattern).

Nā Hoʻonā ʻano Kernel

No ka hoʻopau ʻana i nā kumuwaiwai i ka loop loop, e ʻike kākou pehea e hiki ai iā mākou ke pale i kahi pae. ʻO ia hoʻi, ke hoʻomau nei i kā mākou hiʻohiʻona, e ʻike kākou pehea e hoʻomoe ai ka waiter i ke akeakamai a hoʻāla iā ia inā pono. ʻO ka mea mua, e nānā kākou pehea e hana ai i kēia ma o ke ʻano kernel o ka ʻōnaehana hana. ʻOi aku ka lohi o nā hale a pau ma laila ma mua o nā mea ma kahi hoʻohana. He mau manawa lohi, no ka laʻana AutoResetEvent 53 manawa lohi SpinLock [Richter]. Akā me kā lākou kōkua, hiki iā ʻoe ke hoʻonohonoho i nā kaʻina hana ma ka ʻōnaehana, hoʻokele a ʻaʻole paha.

ʻO ka hana kumu ma aneʻi ʻo ka semaphore i manaʻo ʻia e Dijkstra ma mua o ka hapalua haneli i hala. ʻO ka semaphore, ʻo ia hoʻi, he integer maikaʻi i mālama ʻia e ka ʻōnaehana, a ʻelua mau hana ma luna o ia mea, hoʻonui a hoʻemi. Inā ʻaʻole e emi iho, ʻaʻole, a laila ua paʻa ke kaula kelepona. Ke hoʻonui ʻia ka helu e kekahi lola/kaʻina hana ʻē aʻe, a laila hoʻokuʻu ʻia nā kaula a hoʻemi hou ʻia ka semaphore e ka helu i hala. Hiki i kekahi ke noʻonoʻo i nā kaʻaahi i loko o kahi bottleneck me kahi semaphore. Hāʻawi ʻo NET i kekahi mau hana me ka hana like: AutoResetEvent, ManualResetEvent, Mutex a naʻu iho Semaphore. E hoʻohana mākou AutoResetEvent, ʻo kēia ka mea maʻalahi o kēia mau hana: ʻelua wale nō waiwai 0 a me 1 (hewa, ʻoiaʻiʻo). Kona Hana WaitOne() poloka i ke kaula kelepona inā he 0 ka waiwai, a inā he 1, e hoʻohaʻahaʻa ia i ka 0 a lele. He ʻano hana Set() piʻi aʻe i ka 1 a hoʻokuʻu i hoʻokahi waiter i loko, nāna e hoʻohaʻahaʻa hou i ka 0. Hana ʻia e like me ka turnstile subway.

E hoʻopili mākou i ka hoʻonā a hoʻohana i ka laka no kēlā me kēia loea, ʻaʻole no nā mea āpau i ka manawa hoʻokahi. ʻO kēlā mau mea. i kēia manawa, hiki ke loaʻa i kekahi mau akeakamai i ka manawa hoʻokahi, ʻaʻole hoʻokahi. Akā ke ālai hou nei mākou i ke komo ʻana i ka papaʻaina i mea e pono ai, e pale aku i nā heihei (kūlana lāhui), lawe 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();
}

No ka hoʻomaopopo ʻana i ka mea e hana nei ma ʻaneʻi, e noʻonoʻo ʻoe i ka hihia i ka wā i lawe ʻole ai ke akeakamai i nā ʻōpala, a laila e like kāna mau hana. Ke kali nei ʻo ia no ke komo ʻana i ka papaʻaina. Loaʻa iā ia, hoʻāʻo ʻo ia e lawe i nā ʻōpala. ʻAʻole i hana. Hāʻawi ia i ke komo i ka papaʻaina (mutual exclusion). A hala i kāna "turnstile" (AutoResetEvent) (ua wehe mua lākou). Komo hou i ka pōʻai, no ka mea ʻaʻohe ona mākia. Hoʻāʻo ʻo ia e lawe iā lākou a kū i kāna "turnstile". ʻO kekahi hoa ʻoi aku ka pōmaikaʻi ma ka ʻākau a hema paha, i ka pau ʻana o ka ʻai ʻana, wehe ʻo ia i kā mākou akeakamai, "e wehe ana i kāna turnstile." ʻO kā mākou akeakamai i hala (a pani ʻia ma hope) no ka lua o ka manawa. Hoʻāʻo ʻo ia no ke kolu o ka manawa e lawe i nā ʻōpala. Pōmaikaʻi iā ʻoe. A hele ʻo ia i kāna turnstile e ʻai ai.

Ke loaʻa nā hewa maʻamau i loko o ia code (ke mau nei lākou), no ka laʻana, ua kuhi hewa ʻia kahi hoalauna a i ʻole hana ʻia ka mea like. AutoResetEvent no nā mea a pau (Enumerable.Repeat), a laila e kali ka poʻe akeakamai i nā mea hoʻomohala, no ka mea He hana paʻakikī ka ʻimi ʻana i nā hewa ma ia code. ʻO kekahi pilikia me kēia hoʻonā ʻaʻole ia e hōʻoiaʻiʻo ʻaʻole e pōloli kekahi poʻe akeakamai.

Hoʻoponopono Hybrid

Ua nānā mākou i ʻelua ala e pili ana i ka manawa, ke noho mākou i ke ʻano mea hoʻohana a me ka loop, a i ka wā e pale ai mākou i ke kaula ma ka kernel. He maikaʻi ke ala mua no nā laka pōkole, ʻo ka lua no nā lōʻihi. Pono pinepine e kali mua no ka hoʻololi ʻana o kahi loli i loko o kahi loop, a laila e kāohi i ke kaula ke lōʻihi ka kali ʻana. Hoʻokō ʻia kēia ala i ka mea i kapa ʻia. hale huila. Eia nā hana like me ke ʻano kernel, akā i kēia manawa me kahi loop mode mea hoʻohana: SemaphorSlim, ManualResetEventSlim etc. ʻO ka hoʻolālā kaulana loa ma ʻaneʻi Monitor, no ka mea ma C# aia kahi kaulana lock huaʻōlelo. Monitor ʻO kēia ka semaphore like me ka nui o ka waiwai o 1 (mutex), akā me ke kākoʻo no ke kali ʻana i kahi loop, recursion, ka Condition Variable pattern (ʻoi aʻe ma luna o lalo), etc. E nānā i kahi hopena me ia.

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

Eia ke ālai hou nei mākou i ka papaʻaina holoʻokoʻa no ke komo ʻana i nā ʻōpala, akā i kēia manawa ke wehe nei mākou i nā kaula āpau i ka manawa hoʻokahi, ʻaʻole nā ​​hoalauna ke pau ka ʻai ʻana o kekahi. ʻO kēlā mau mea. ʻO ka mua, ʻai kekahi a poloka i nā hoalauna, a i ka pau ʻana o kēia mea, makemake ʻo ia e ʻai hou i kēia manawa, hele ʻo ia i ka poloka a hoʻāla i kona mau hoalauna, no ka mea. ua emi kona manawa kali.

ʻO kēia ke ala e pale aku ai mākou i nā deadlocks a me ka pōloli o kekahi poʻe akeakamai. Hoʻohana mākou i kahi loop no kahi kali pōkole a hoʻopaʻa i ke kaula no kahi lōʻihi. ʻOi aku ka lohi o ka wehe ʻana i nā mea a pau ma mua o ka wehe ʻana o ka hoalauna, e like me ka hoʻonā me AutoResetEvent, aka, aole e nui ka okoa, no ka mea pono e noho mua nā pae ma ke ʻano mea hoʻohana.

У lock Loaʻa i ka syntax nā mea haohao. Manaʻo e hoʻohana Monitor pololei [Richter] [Eric Lippert]. ʻO kekahi o lākou kēlā lock mau ma waho o Monitor, ʻoiai inā he ʻokoʻa, ʻo ia ka mea hiki ke hoʻololi i kahi pae ʻē aʻe i ke kūlana hoʻomanaʻo like. Ma ia mau hihia, ʻoi aku ka maikaʻi o ka hele ʻana i ka deadlock a i ʻole e hoʻopau palekana i ka papahana. ʻO kekahi mea kupanaha ʻo ia ka hoʻohana ʻana o Monitor i nā poloka synchronization (SyncBlock), aia i loko o nā mea a pau. No laila, inā koho ʻia kahi mea kūpono ʻole, hiki iā ʻoe ke loaʻa i kahi deadlock (no ka laʻana, inā ʻoe e laka i kahi kaula interned). Hoʻohana mākou i ka mea huna mau no kēia.

Hāʻawi ka Condition Variable pattern iā ʻoe e hoʻokō maʻalahi i ka manaʻolana o kekahi kūlana paʻakikī. Ma .NET, ʻaʻole i piha, i koʻu manaʻo, no ka mea ma ke kumumanaʻo, pono e nui nā queues ma nā ʻano like ʻole (e like me nā Posix Threads), ʻaʻole ma kahi lok. A laila hiki i kekahi ke hana ia mau mea no ka poʻe akeakamai a pau. Akā i loko o kēia ʻano, hiki iā ʻoe ke hōʻemi i ke code.

nui ka poe akeakamai a i ole async / await

ʻAe, hiki iā mākou ke hoʻopaʻa pono i nā pae. Akā, pehea inā he nui ka poʻe akeakamai? 100? 10000? No ka laʻana, loaʻa iā mākou nā noi 100000 i ka kikowaena pūnaewele. E lilo ia i luna e hana i kahi pae no kēlā me kēia noi, no ka mea ʻaʻole e holo like nā loina he nui. E holo wale ana e like me ka nui o nā cores logical (he 4 kaʻu). A e lawe wale nā ​​mea ʻē aʻe i nā kumuwaiwai. ʻO kahi hoʻonā i kēia pilikia ʻo ke ʻano async / await. ʻO kona manaʻo ʻaʻole paʻa ka hana i ke kaula inā pono ia e kali i kahi mea e hoʻomau. A ke hana ʻo ia i kekahi mea, hoʻomaka hou ia i kāna hoʻokō (akā ʻaʻole pono ma ka pae hoʻokahi!). I kā mākou hihia, e kali mākou i ka ʻōpala.

SemaphoreSlim aia no keia WaitAsync() ʻano hana. Eia kahi hoʻokō me ka hoʻohana ʻana i kēia ʻano.

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

Keʻano me async / await unuhi ʻia i loko o kahi mīkini mokuʻāina paʻakikī e hoʻihoʻi koke i kona kūloko Task. Ma o ia, hiki iā ʻoe ke kali i ka pau ʻana o ke ʻano, hoʻopau iā ia, a me nā mea ʻē aʻe āu e hana ai me Task. I loko o ke ʻano, hoʻokele ka mīkini mokuʻāina i ka hoʻokō. ʻO ka laina lalo inā ʻaʻohe lohi, a laila ua like ka hoʻokō ʻana, a inā aia, a laila hoʻokuʻu ʻia ke kaula. No ka hoʻomaopopo maikaʻi ʻana i kēia, ʻoi aku ka maikaʻi o ka nānā ʻana i kēia mīkini mokuʻāina. Hiki iā ʻoe ke hana i nā kaulahao mai kēia mau mea async / await ʻano hana.

E hoao kakou. Ka hana a 100 poʻe akeakamai ma ka mīkini me 4 logical cores, 8 kekona. ʻO ka hopena mua me Monitor wale nō i holo i nā pae mua 4 a ʻo ke koena ʻaʻole holo iki. ʻO kēlā me kēia o kēia mau kaula 4 i hoʻohana ʻole ʻia ma kahi o 2ms. A holo ka hopena async / kali i nā 100 a pau, me ka awelika kali o 6.8 kekona i kēlā me kēia. ʻOiaʻiʻo, i nā ʻōnaehana maoli, ʻaʻole i ʻae ʻia ka palaualelo no 6 kekona a ʻoi aku ka maikaʻi ʻaʻole e hana i nā noi he nui e like me kēia. ʻAʻole hiki ke hoʻonui ʻia ka hopena me Monitor.

hopena

E like me kāu e ʻike ai mai kēia mau hiʻohiʻona liʻiliʻi, kākoʻo ʻo .NET i nā hana hoʻonohonoho like ʻole. Eia naʻe, ʻaʻole maopopo i ka pehea e hoʻohana ai iā lākou. Manaʻo wau ua kōkua kēia ʻatikala. I kēia manawa, ʻo ia ka hopena, akā he nui nā mea hoihoi i koe, no ka laʻana, nā hōʻiliʻili palekana thread, TPL Dataflow, Reactive programming, Software Transaction model, etc.

Pūnaewele

Source: www.habr.com

Pākuʻi i ka manaʻo hoʻopuka