Ake sibone ukuthi ukuhlela okuhambisanayo nokuhambisanayo kusebenza kanjani ku-.Net, kusetshenziswa i-Philosophers Dining Problem njengesibonelo. Uhlelo yilo, kusukela ekuvumelaneni kwemicu / izinqubo, kuya kumodeli yomlingisi (ezingxenyeni ezilandelayo). Lesi sihloko singase sibe usizo kumuntu wokuqala omaziyo noma ukuze sivuselele ulwazi lwakho.
Kungani ukwenza? Ama-Transistors afinyelela ubukhulu bawo obuncane, umthetho kaMoore uncike ekukhawulweni kwejubane lokukhanya ngakho-ke ukwanda kubonakala enanini, ama-transistors amaningi angenziwa. Ngesikhathi esifanayo, inani ledatha liyakhula, futhi abasebenzisi balindele impendulo esheshayo evela kumasistimu. Esimeni esinjalo, ukuhlela "okujwayelekile", lapho sinentambo eyodwa yokukhipha, ayisasebenzi. Udinga ngandlela thize ukuxazulula inkinga yokubulawa ngesikhathi esisodwa noma ngesikhathi esisodwa. Ngaphezu kwalokho, le nkinga ikhona emazingeni ahlukene: ezingeni lezintambo, ezingeni lezinqubo, ezingeni lemishini kunethiwekhi (izinhlelo ezisatshalaliswa). I-.NET inobuchwepheshe obusezingeni eliphezulu, obuhlolwe isikhathi bokuxazulula izinkinga ezinjalo ngokushesha nangempumelelo.
Inhloso
U-Edsger Dijkstra ubeke le nkinga kubafundi bakhe kusukela ngo-1965. Ukwakhiwa okusunguliwe kungokulandelayo. Kukhona inombolo ethile (imvamisa emihlanu) yezazi zefilosofi kanye nenani elifanayo lezimfoloko. Bahlala etafuleni eliyindilinga, kukhona izimfoloko phakathi kwabo. Izazi zefilosofi zingadla emapuletini azo okudla okungapheli, zicabange noma zilinde. Ukuze udle isazi sefilosofi, udinga ukuthatha izimfoloko ezimbili (owokugcina wabelana ngemfoloko neyokuqala). Ukucosha nokubeka phansi imfoloko kuyizenzo ezimbili ezihlukene. Zonke izazi zefilosofi zithule. Umsebenzi ukuthola i-algorithm enjalo ukuthi bonke bangacabanga futhi bagcwale ngisho nangemva kweminyaka engama-54.
Okokuqala, ake sizame ukuxazulula le nkinga ngokusebenzisa indawo okwabelwana ngayo. Izimfoloko zilele etafuleni elivamile futhi izazi zefilosofi zivele zizithathe lapho zikhona bese ziwabuyisela emuva. Lapha kunezinkinga ngokuvumelanisa, nini ngempela ukuthatha ama-surebets? kuthiwani uma ingekho imfoloko? njll. Kodwa okokuqala, ake siqale izazi zefilosofi.
Ukuqala imicu, sisebenzisa i-thread pool ngokusebenzisa Task.Run
indlela:
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);
}
I-thread pool yakhelwe ukuthuthukisa ukudalwa nokususwa kwentambo. Leli chibi linomugqa onemisebenzi futhi i-CLR idala noma isuse imicu kuye ngenani lale misebenzi. Iphuli eyodwa yawo wonke ama-AppDomains. Leli chibi kufanele lisetshenziswe cishe njalo, ngoba. asikho isidingo sokuzihlupha ngokudala, ukususa izintambo, imigqa yazo, njll. Kungenzeka ngaphandle kwechibi, kodwa-ke kufanele ulisebenzise ngokuqondile. Thread
, lokhu kuyasiza ezimweni lapho udinga ukushintsha okubalulekile kochungechunge, lapho sinomsebenzi omude, wentambo yangaphambili, njll.
Ngamanye amazwi, System.Threading.Tasks.Task
ikilasi liyafana Thread
, kodwa ngazo zonke izinhlobo zokunethezeka: ikhono lokuqhuba umsebenzi ngemva kwebhlokhi yeminye imisebenzi, ukuyibuyisela emisebenzini, ukuyiphazamisa kalula, nokuningi. njll. Ziyadingeka ukuze zisekele ukwakhiwa kwe-async / ukulinda (Iphethini ye-Asynchronous esekelwe kumsebenzi, ushukela we-syntactic wokulinda ukusebenza kwe-IO). Sizokhuluma ngalokhu kamuva.
CancelationTokenSource
lapha kuyadingeka ukuze intambo ikwazi ukuziqeda ngokwayo ngesignali yentambo yokubiza.
Vumelanisa Izinkinga
Izazi Zefilosofi Ezivinjiwe
Kulungile, siyayazi indlela yokwenza imicu, ake sizame ukudla kwasemini:
// ΠΡΠΎ ΠΊΠ°ΠΊΠΈΠ΅ Π²ΠΈΠ»ΠΊΠΈ Π²Π·ΡΠ». Π ΠΏΡΠΈΠΌΠ΅ΡΡ: 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();
}
}
Lapha siqala sizama ukuthatha imfoloko yesokunxele, bese kuba imfoloko efanele, futhi uma isebenza, bese siyadla futhi siyibuyisele. Ukuthatha imfoloko eyodwa kuyi-athomu, i.e. imicu emibili ayikwazi ukuthatha eyodwa ngesikhathi esisodwa (engalungile: eyokuqala ifunda ukuthi imfoloko ikhululekile, eyesibili - futhi, eyokuqala ithatha, eyesibili ithatha). Kwalokhu Interlocked.CompareExchange
, okufanele isetshenziswe ngomyalelo wokucubungula (TSL
, XCHG
), evala ingxenye yenkumbulo yokufunda nokubhala ngokulandelana kwe-athomu. Futhi i-SpinWait ilingana nokwakhiwa while(true)
kuphela "ngomlingo" omncane - intambo ithatha iprosesa (Thread.SpinWait
), kodwa ngezinye izikhathi idlulisela ukulawula kolunye uchungechunge (Thread.Yeild
) noma alale (Thread.Sleep
).
Kodwa lesi sixazululo asisebenzi, ngoba ukugeleza ngokushesha (kimi phakathi nomzuzwana) kuvinjiwe: zonke izazi zefilosofi zithatha imfoloko yazo yesokunxele, kodwa hhayi elungile. Amalungu afanayo amafoloko abe namanani: 1 2 3 4 5.
Emfanekisweni, izintambo zokuvimbela (i-deadlock). Okuluhlaza - ukubulawa, okubomvu - ukuvumelanisa, okumpunga - intambo ilele. Ama-rhombuse akhombisa isikhathi sokuqala Semisebenzi.
Indlala Yezazi Zefilosofi
Nakuba akudingekile ukucabanga ikakhulukazi ukudla okuningi, kodwa indlala yenza noma ubani alahle ifilosofi. Ake sizame ukulingisa isimo sokulamba kwemicu enkingeni yethu. Indlala yilapho intambo isebenza, kodwa ngaphandle komsebenzi obalulekile, ngamanye amazwi, lokhu kufana ne-deadlock, kuphela manje intambo ayilali, kodwa ibheka ngenkuthalo into engayidla, kodwa akukho ukudla. Ukuze sigweme ukuvimba njalo, sizobuyisela imfoloko uma singakwazi ukuthatha enye.
// Π’ΠΎ ΠΆΠ΅ ΡΡΠΎ ΠΈ Π² 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)
);
}
Okubalulekile ngale khodi ukuthi izazi zefilosofi ezimbili kwezine ziyakhohlwa ukubeka phansi imfoloko yazo yangakwesobunxele. Futhi kuvela ukuthi badla ukudla okuningi, kuyilapho abanye beqala ukulamba, nakuba imicu inokubaluleka okufanayo. Lapha abalambi ngokuphelele, ngoba. izazi zefilosofi ezimbi zibuyisela izimfoloko zazo emuva ngezinye izikhathi. Kuvela ukuthi abantu abalungile badla cishe izikhathi ezi-5 kunababi. Ngakho iphutha elincane kukhodi liholela ekwehleni kokusebenza. Kuyafaneleka futhi ukuqaphela lapha ukuthi isimo esingavamile singenzeka lapho zonke izazi zefilosofi zithatha imfoloko yesokunxele, akukho kwesokudla, babeka kwesokunxele, balinde, bathathe kwesokunxele futhi, njll. Lesi simo naso siyindlala, sifana ne-deadlock. Ngehlulekile ukuyiphinda. Ngezansi isithombe sesimo lapho izazi zefilosofi ezimbi zithathe zombili izimfoloko kanti ezimbili ezinhle zibulawa yindlala.
Lapha ungabona ukuthi izintambo zivuka ngezinye izikhathi bese uzama ukuthola insiza. Ama-cores amabili kwamane awenzi lutho (igrafu eluhlaza ngenhla).
Ukufa Kwesazi Sefilosofi
Nokho, enye inkinga engaphazamisa isidlo sakusihlwa esikhazimulayo sezazi zefilosofi uma omunye wabo efa ngokuzumayo ephethe izimfoloko ezandleni zakhe (futhi bazomngcwaba kanjalo). Khona omakhelwane bazosala bengadlile. Ungakwazi ukuza nekhodi eyisibonelo yaleli cala ngokwakho, isibonelo, ilahliwe NullReferenceException
ngemva kokuba isazi sefilosofi sithathe izimfoloko. Futhi, ngendlela, okuhlukile ngeke kuphathwe futhi ikhodi yocingo ngeke nje ibambe (ngenxa yalokhu AppDomain.CurrentDomain.UnhandledException
futhi njll.). Ngakho-ke, izibambi zamaphutha ziyadingeka entanjeni ngokwazo futhi ngokunqanyulwa okuhle.
Weta
Kulungile, siyixazulula kanjani le nkinga yokufa, indlala, kanye nokufa? Sizovumela isazi sefilosofi esisodwa kuphela ukuthi sifinyelele izimfoloko, singeze ukukhishwa okufanayo kochungechunge lwale ndawo. Kwenziwa kanjani? Ake sithi kunoweta eduze kwezazi zefilosofi onikeza noma yisiphi isazi sefilosofi imvume yokuthatha izimfoloko. Senza kanjani lesi sikhonzi nokuthi izazi zefilosofi zizombuza kanjani, imibuzo iyathakazelisa.
Indlela elula yilapho izazi zefilosofi zizomane zibuze njalo uweta ukuze zifinyelele izimfoloko. Labo. manje amafilosofi ngeke alinde imfoloko eduze, kodwa alinde noma abuze uweta. Ekuqaleni, sisebenzisa kuphela Isikhala somsebenzisi salokhu, kuso asisebenzisi iziphazamiso ukubiza noma yiziphi izinqubo kusuka ku-kernel (mayelana nazo ngezansi).
Izixazululo endaweni yomsebenzisi
Lapha sizokwenza okufanayo njengoba sasivame ukwenza ngemfoloko eyodwa kanye nezazi zefilosofi ezimbili, sizojikeleza ngomjikelezo bese silinda. Kodwa manje kuzoba zonke izazi zefilosofi futhi, njengokungathi, imfoloko eyodwa kuphela, i.e. kungashiwo ukuthi yisazi sefilosofi kuphela esathatha le βmfoloko yegolideβ kuweta ezodla. Kulokhu sisebenzisa 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
lokhu kuyisivimbeli, ngokulingana, okufanayo while(true) { if (!lock) break; }
, kodwa "ngomlingo" owengeziwe kunaku-in SpinWait
(esetshenziswa lapho). Manje useyakwazi ukubala abalindile, abalalise kancane, nangaphezulu. njll. Ngokuvamile, yenza konke okusemandleni ukuthuthukisa. Kodwa kufanele sikhumbule ukuthi lokhu kusewumjikelezo ofanayo osebenzayo odla izinsiza zeprosesa futhi ugcine ukugeleza, okungaholela ekulambeni uma omunye wezazi zefilosofi eba obaluleke kakhulu kunabanye, kodwa engenayo imfoloko yegolide (Inkinga ye-Priority Inversion) . Ngakho-ke, siyisebenzisela izinguquko ezimfushane kakhulu kwinkumbulo eyabiwe kuphela, ngaphandle kwezingcingo zezinkampani zangaphandle, izingidi ezifakwe esidlekeni, nezinye izimanga.
Umdwebo we SpinLock
. Imifudlana "ilwela" njalo imfoloko yegolide. Kukhona ukwehluleka - kumfanekiso, indawo ekhethiwe. Ama-cores awasetshenziswa ngokugcwele: cishe 2/3 kuphela ngale micu emine.
Esinye isixazululo lapha kungaba ukusebenzisa kuphela Interlocked.CompareExchange
ngokulinda okufanayo okusebenzayo njengoba kukhonjisiwe kukhodi engenhla (kuzazi zefilosofi ezilambile), kodwa lokhu, njengoba sekushiwo, kungase kuholele ekuvimbeni.
Mayelana Interlocked
Kufanele kuqashelwe ukuthi akukhona kuphela CompareExchange
, kodwa nezinye izindlela zokufunda NOKUBHALA nge-athomu. Futhi ngokuphindaphindiwe koshintsho, uma kwenzeka enye intambo inesikhathi sokwenza izinguquko zayo (funda 1, funda 2, bhala 2, bhala 1 yimbi), ingasetshenziselwa izinguquko eziyinkimbinkimbi kwinani elilodwa (I-Interlocked Anything iphethini) .
Izixazululo Zemodi Ye-Kernel
Ukugwema ukumosha izinsiza ku-loop, ake sibone ukuthi singawuvimba kanjani uchungechunge. Ngamanye amazwi, siqhubeka nesibonelo sethu, ake sibone ukuthi uweta usilalisa kanjani isazi sefilosofi futhi asivuse kuphela lapho kudingekile. Okokuqala, ake sibheke ukuthi singakwenza kanjani lokhu ngemodi ye-kernel yesistimu yokusebenza. Zonke izakhiwo lapho zivame ukuhamba kancane kunalezo ezisesikhaleni somsebenzisi. Izikhathi ezimbalwa ezihamba kancane, isibonelo AutoResetEvent
mhlawumbe 53 izikhathi kancane SpinLock
[Richter]. Kodwa ngosizo lwabo, ungakwazi ukuvumelanisa izinqubo kulo lonke uhlelo, oluphethwe noma cha.
Ukwakhiwa okuyisisekelo lapha i-semaphore ephakanyiswe yi-Dijkstra eminyakeni engaphezu kwekhulu leminyaka edlule. I-semaphore, kalula nje, iyinani eliphelele eliphethwe uhlelo, kanye nokusebenza okubili kulo, ukukhuphuka nokuncipha. Uma ihluleka ukuncipha, uziro, khona-ke uchungechunge lokushaya luyavinjwa. Uma inombolo inyuswa ngenye intambo/inqubo esebenzayo, imicu iyayeqiwa futhi i-semephore iphinde yehliswe ngenombolo edlulisiwe. Umuntu angacabanga izitimela ebhodleleni eline-semaphore. I-.NET inikeza izakhiwo ezimbalwa ezinomsebenzi ofanayo: AutoResetEvent
, ManualResetEvent
, Mutex
nami uqobo Semaphore
. Sizosebenzisa AutoResetEvent
, lokhu kulula kakhulu kulezi zakhiwo: amanani amabili kuphela 0 no-1 (amanga, iqiniso). Indlela Yakhe WaitOne()
ivimba intambo yokubiza uma inani lalingu-0, futhi uma u-1, liyehlisela ku-0 futhi liyeqe. Indlela Set()
ikhuphukela ku-1 futhi idedela uweta oyedwa, ophinde wehlele ku-0. Isebenza njengesitimela esingaphansi komhlaba.
Masixabanise isixazululo futhi sisebenzise isikhiya sefilosofi ngayinye, hhayi sonke ngesikhathi esisodwa. Labo. manje kungaba khona izazi zefilosofi eziningana ngesikhathi esisodwa, hhayi eyodwa. Kodwa siphinde sivimbe ukufinyelela etafuleni ukuze senze kahle, sigweme izinhlanga (izimo zomjaho), sithathe ama-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();
}
Ukuze uqonde ukuthi kwenzekani lapha, cabangela icala lapho isazi sefilosofi sihluleka ukuthatha izimfoloko, khona-ke izenzo zakhe zizoba kanje. Ulindele ukungena etafuleni. Eseyitholile, azame ukuthatha izimfoloko. Akusebenzanga. Inikeza ukufinyelela kuthebula (ukukhishwa okufanayo). Futhi udlula "ijika" lakhe (AutoResetEvent
) (ziqale zivuliwe). Ingena emjikelezweni futhi, ngoba akanazo izimfoloko. Azame ukuwathatha ame "ejikajika" lakhe. Omunye umakhelwane onenhlanhla kwesokudla noma kwesobunxele, eseqedile ukudla, uvula isazi sefilosofi yethu, "evula i-turnstile yakhe." Isazi sefilosofi yethu siyayidlulisa (futhi ivale ngemuva kwayo) okwesibili. Azame okwesithathu ukuthatha izimfoloko. Ngikufisela inhlanhla. Futhi udlulisa ijika lakhe ukuze adle.
Uma kukhona amaphutha angahleliwe kukhodi enjalo (ahlala ekhona), isibonelo, umakhelwane uchazwe ngokungalungile noma kwakhiwa into efanayo. AutoResetEvent
kwabo bonke (Enumerable.Repeat
), khona-ke izazi zefilosofi zizolinda abathuthukisi, ngoba Ukuthola amaphutha kukhodi enjalo kuwumsebenzi onzima. Enye inkinga ngalesi sixazululo ukuthi akuqinisekisi ukuthi esinye isazi sefilosofi ngeke silambe.
Izixazululo ze-Hybrid
Sibheke izindlela ezimbili zokubala isikhathi, lapho sihlala kumodi yomsebenzisi ne-loop, nalapho sivimba uchungechunge ngekernel. Indlela yokuqala ilungele ukukhiya okufushane, okwesibili kumade. Ngokuvamile kuyadingeka ukuthi uqale ulinde kafushane ukushintshashintsha ku-loop, bese uvimba intambo lapho ukulinda kukude. Le ndlela isetshenziswa kulokho okubizwa ngokuthi. izakhiwo ezixubile. Nazi izinto ezifanayo zokwakha njengemodi ye-kernel, kodwa manje nge-loop yemodi yomsebenzisi: SemaphorSlim
, ManualResetEventSlim
njll. Umklamo odume kakhulu lapha Monitor
, ngoba ku C# kukhona owaziwayo lock
i-syntax. Monitor
lena i-semaphore efanayo enenani eliphakeme elingu-1 (mutex), kodwa ngokusekelwa kokulinda ku-loop, i-recursion, iphethini ye-Condition Variable (ngaphezulu kulokho ngezansi), njll. Ake sibheke isisombululo ngakho.
// Π‘ΠΏΡΡΡΠ΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ Π΄Π»Ρ ΠΠΎΠ½ΠΈΡΠΎΡΠ° ΠΎΡ Π²ΡΠ΅Ρ
, ΡΡΠΎΠ±Ρ Π±Π΅Π· Π΄Π΅Π΄Π»ΠΎΠΊΠΎΠ².
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);
}
}
Lapha siphinda sivimba itafula lonke ukuze sifinyelele izimfoloko, kodwa manje sivula yonke imicu ngesikhathi esisodwa, hhayi omakhelwane uma umuntu eqeda ukudla. Labo. kuqale umuntu adle avimbe omakhelwane kuthi uma eqeda lokhu kodwa aphinde adle khona manjalo angene avimbe avuse omakhelwane ngoba. isikhathi sayo sokulinda sincane.
Yile ndlela esigwema ngayo imigoqo kanye nendlala yesazi esithile sefilosofi. Sisebenzisa i-loop ukulinda okufushane futhi sivimba intambo isikhathi eside. Ukuvulela wonke umuntu ngesikhathi esisodwa kuhamba kancane kunalokho uma umakhelwane evuliwe, njengasesixazululweni ngo AutoResetEvent
, kodwa umehluko akufanele ube mkhulu, ngoba imicu kufanele ihlale kumodi yomsebenzisi kuqala.
Π£ lock
I-syntax inezimanga ezimbi. Ncoma ukusebenzisa Monitor
ngqo [Richter] [Eric Lippert]. Enye yazo yilokho lock
njalo ngaphandle Monitor
, noma ngabe bekukhona okuhlukile, lapho enye intambo ingashintsha isimo sememori eyabiwe. Ezimweni ezinjalo, kuvame ukuba ngcono ukuya ku-deadlock noma ukunqamula uhlelo ngokuphepha. Okunye okumangazayo ukuthi i-Monitor isebenzisa amabhulokhi okuvumelanisa (SyncBlock
), ezikhona kuzo zonke izinto. Ngakho-ke, uma kukhethwa into engafanele, ungathola kalula i-deadlock (isibonelo, uma ukhiyela ochungechungeni oluhlanganisiwe). Sisebenzisa into ehlale ifihliwe kulokhu.
Iphethini ye-Condition Variable ikuvumela ukuthi usebenzise kafushane okulindelekile kwesimo esithile esiyinkimbinkimbi. Ku-NET, ayiphelele, ngombono wami, ngoba ngokombono, kufanele kube nemigqa eminingana kokuguquguqukayo okuningana (njengaku-Posix Threads), hhayi ku-lok eyodwa. Khona-ke umuntu angabenzela zonke izazi zefilosofi. Kodwa nakuleli fomu, likuvumela ukuthi unciphise ikhodi.
izazi zefilosofi eziningi noma async
/ await
Kulungile, manje singavimba uchungechunge ngempumelelo. Kodwa kuthiwani uma sinezazi zefilosofi eziningi? 100? 10000? Isibonelo, sithole izicelo eziyi-100000 kuseva yewebhu. Kuzoba phezulu ukudala intambo yesicelo ngasinye, ngoba imicu eminingi kangaka ngeke isebenze ngokufana. Izogijima kuphela njengoba kunama-core cores (ngina-4). Futhi bonke abanye bazovele bathathe izinsiza. Isixazululo esisodwa sale nkinga iphethini ye-async / yokulinda. Umbono wayo ukuthi umsebenzi awubambi intambo uma idinga ukulinda okuthile ukuze kuqhubeke. Futhi lapho yenza okuthile, iqala kabusha ukubulawa kwayo (kodwa hhayi emculweni ofanayo!). Esimeni sethu, sizolinda imfoloko.
SemaphoreSlim
inalokhu WaitAsync()
indlela. Nakhu ukuqaliswa kusetshenziswa le phethini.
// ΠΠ°ΠΏΡΡΠΊ ΡΠ°ΠΊΠΎΠΉ ΠΆΠ΅, ΠΊΠ°ΠΊ ΡΠ°Π½ΡΡΠ΅. ΠΠ΄Π΅-Π½ΠΈΠ±ΡΠ΄Ρ Π² ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ΅:
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();
}
Indlela nge async
/ await
ihunyushwa emshinini wesimo okhohlisayo obuyisela ngokushesha okungaphakathi kwawo Task
. Ngayo, ungalinda ukuqedwa kwendlela, uyikhansele, nakho konke okunye ongakwenza nge-Task. Ngaphakathi kwendlela, umshini wombuso ulawula ukubulawa. Okubalulekile ukuthi uma kungekho ukubambezeleka, khona-ke ukubulawa kuyavumelana, futhi uma kukhona, khona-ke intambo iyakhululwa. Ukuze uqonde kangcono lokhu, kungcono ukubheka lo mshini wombuso. Ungakha amaketanga kusuka kulawa async
/ await
izindlela.
Ake sihlole. Umsebenzi wezazi zefilosofi eziyi-100 emshinini onamaphuzu angu-4 anengqondo, imizuzwana engu-8. Isixazululo sangaphambilini esine-Monitor sisebenzise imicu yokuqala emi-4 kuphela futhi eminye ayizange isebenze nhlobo. Ngayinye yale micu emi-4 ibingasebenzi isikhathi esingaba ngu-2ms. Futhi isixazululo se-async / sokulinda sisebenze wonke angu-100, ngokulinda okuphakathi kwamasekhondi angu-6.8 lilinye. Vele, ezinhlelweni zangempela, ukungenzi lutho imizuzwana eyi-6 akwamukelekile futhi kungcono ukungacubunguli izicelo eziningi ezinjengalezi. Isixazululo nge-Monitor sibonakale singenamandla neze.
isiphetho
Njengoba ubona kulezi zibonelo ezincane, i-.NET isekela ukwakha okuningi kokuvumelanisa. Nokho, akubonakali ngaso sonke isikhathi ukuthi zisetshenziswa kanjani. Ngithemba ukuthi lesi sihloko sibe usizo. Okwamanje, lokhu ukuphela, kodwa kusekhona izinto eziningi ezithakazelisayo ezisele, isibonelo, amaqoqo aphephile ngentambo, i-TPL Dataflow, i-Reactive programming, imodeli ye-Software Transaction, njll.
Imithombo
- Ukugeleza ngeso lengqondo:
Concurrency Visualizer - I-MSDN:
Ukunyathela ,Amaphethini wokuhlela asynchronous nabanye abaningi. abanye - [Richter] - CLR nge-C#, uJeffrey Richter
- [U-Eric Lippert] -
Mayelana nokukhiya Umthombo - Isithombe - "Dansa phakathi kwezinkemba", G. Semiradsky
Source: www.habr.com