Makhe sibone ukuba udweliso lwenkqubo oluhambelanayo lusebenza njani kwi-.Net, usebenzisa iPhilosophers Dining Problem njengomzekelo. Isicwangciso yilesi, ukusuka kungqamaniso lwemicu / iinkqubo, kwimodeli yomdlali (kwezi nxalenye zilandelayo). Eli nqaku linokuba luncedo kumntu omaziyo okanye ukuhlaziya ulwazi lwakho.
Kutheni usenza nje? I-Transistors ifikelela kubukhulu babo obuncinci, umthetho kaMoore uxhomekeke ekunciphiseni isantya sokukhanya kwaye ngoko ke ukunyuka kubonwa kwinani, ii-transistors ezininzi zingenziwa. Ngexesha elifanayo, inani ledatha likhula, kwaye abasebenzisi balindele impendulo ngokukhawuleza kwiinkqubo. Kwimeko enjalo, inkqubo "eqhelekileyo", xa sinentambo enye yokuqhuba, ayisasebenzi. Kufuneka uyisombulule ingxaki yokwenziwa ngaxeshanye okanye ngaxeshanye. Ngaphezu koko, le ngxaki ikhona kumanqanaba ahlukeneyo: kwinqanaba leentambo, kwinqanaba leenkqubo, kwinqanaba loomatshini kwinethiwekhi (iinkqubo ezisasazwayo). I-NET inomgangatho ophezulu, itekhnoloji evavanywe ixesha lokusombulula ngokukhawuleza nangokufanelekileyo iingxaki ezinjalo.
Injongo
U-Edsger Dijkstra ubeke le ngxaki kubafundi bakhe kwangoko ngo-1965. Uqulunqo olusekiweyo lulandelayo. Kukho inani elithile (elihlala lihlanu) lefilosofi kunye nenani elifanayo leefolokhwe. Bahlala kwitafile engqukuva, kukho iifolokhwe phakathi kwabo. Iifilosofi zinokutya kwiipleyiti zabo zokutya okungapheliyo, ukucinga okanye ukulinda. Ukutya ifilosofi, kufuneka uthathe iifolokhwe ezimbini (owokugqibela wabelana ngefoloko kunye neyokuqala). Ukuchola nokubeka phantsi ifolokhwe zizenzo ezimbini ezahlukeneyo. Zonke iintanda-bulumko zithe cwaka. Umsebenzi kukufumana i-algorithm enje ukuba bonke baya kucinga kwaye bazalise emva kweminyaka engama-54.
Okokuqala, makhe sizame ukusombulula le ngxaki ngokusebenzisa indawo ekwabelwana ngayo. Iifolokhwe zilele kwitafile eqhelekileyo kwaye iifilosofi zithatha nje xa zikhona kwaye zibuyisele. Apha kukho iingxaki ngongqamaniso, nini kanye kanye ukuthatha surebets? kuthekani ukuba akukho folokhwe? etc. Kodwa kuqala, makhe siqale iintanda-bulumko.
Ukuqala imisonto, sisebenzisa idama lemisonto 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);
}
Iqula lemisonto yenzelwe ukukhulisa indalo kunye nokucima imisonto. Eli dama linomgca kunye nemisebenzi kwaye i-CLR idala okanye isuse imicu ngokuxhomekeke kwinani lale misebenzi. Iqula elinye kuzo zonke i-AppDomains. Eli dama kufuneka lisetyenziswe phantse rhoqo, kuba. akukho mfuneko yakuzihlupha ngokudala, ukucima imisonto, imigca yabo, njalo njalo. Kuyenzeka ngaphandle kwedama, kodwa ke kufuneka uyisebenzise ngqo. Thread
, oku kuluncedo kumatyala xa ufuna ukutshintsha umba ophambili womsonto, xa sinomsebenzi omde, kumsonto ongePhambili, njl.
Ngamanye amazwi, System.Threading.Tasks.Task
iklasi iyafana Thread
, kodwa ngazo zonke iintlobo eziluncedo: ukukwazi ukuqhuba umsebenzi emva kwebhloko yeminye imisebenzi, ukuyibuyisela kwimisebenzi, ukuyiphazamisa ngokufanelekileyo, kunye nokunye. njl. Ziyafuneka ukuxhasa i-async / ilindele ulwakhiwo (Ipateni esekwe kuMsebenzi weAsynchronous, iswekile elungelelanisiweyo yokulinda imisebenzi ye-IO). Siza kuthetha ngale nto kamva.
CancelationTokenSource
apha iyafuneka ukuze umsonto uziphelise ngokwawo kumqondiso womsonto wokufowuna.
Sync Imiba
Izithandi zobulumko ezivaliweyo
Kulungile, siyayazi indlela yokwenza imisonto, makhe sizame ukuba nesidlo sasemini:
// ΠΡΠΎ ΠΊΠ°ΠΊΠΈΠ΅ Π²ΠΈΠ»ΠΊΠΈ Π²Π·ΡΠ». Π ΠΏΡΠΈΠΌΠ΅ΡΡ: 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();
}
}
Apha siqala sizama ukuthatha ifolokhwe ekhohlo, kwaye emva koko ifoloko elungileyo, kwaye ukuba iyasebenza, ngoko sidla kwaye sibuyisele. Ukuthatha ifolokhwe enye i-athomu, okt. imicu emibini ayikwazi ukuthatha enye ngexesha elinye (engalunganga: eyokuqala ifunda ukuba ifolokhwe ikhululekile, eyesibini - nayo, eyokuqala ithatha, eyesibini ithatha). Yale Interlocked.CompareExchange
, ekufuneka iphunyezwe ngomyalelo weprosesa (TSL
, XCHG
), evala isiqwenga sememori yokufunda nokubhala ngokulandelelana kweathom. Kwaye iSpinWait ilingana nolwakhiwo while(true)
kuphela "ngomlingo" omncinci - umsonto uthatha iprosesa (Thread.SpinWait
), kodwa ngamanye amaxesha idlulisela ulawulo komnye umsonto (Thread.Yeild
) okanye uyalala (Thread.Sleep
).
Kodwa esi sisombululo asisebenzi, kuba ukuhamba ngokukhawuleza (kum ngaphakathi komzuzwana) kuvaliwe: zonke iintanda-bulumko zithatha ifolokhwe yazo yasekhohlo, kodwa hayi ekunene. Uluhlu lwefolokhwe ngoko lunamaxabiso: 1 2 3 4 5.
Kumzobo, ukuvimba imisonto (i-deadlock). Uhlaza - ukubulawa, obomvu - ukuvumelanisa, i-gray - intambo ilele. I-rhombuses ibonisa ixesha lokuqala leMisebenzi.
Indlala yezithandi zobulumko
Nangona kungeyomfuneko ukuba ucinge ngokutya okuninzi, kodwa indlala yenza ukuba nabani na ayeke ifilosofi. Masizame ukulinganisa imeko yendlala yemisonto kwingxaki yethu. Indlala kuxa intambo isebenza, kodwa ngaphandle komsebenzi obalulekileyo, ngamanye amazwi, oku kufana ne-deadlock, kuphela ngoku umsonto awulali, kodwa ujonge ngenkuthalo into yokutya, kodwa akukho kutya. Ukuthintela ukuvaleka rhoqo, siya kuyibuyisela ifolokhwe ukuba asikwazanga 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)
);
}
Into ebalulekileyo malunga nale khowudi kukuba ezimbini zefilosofi ezine zilibale ukubeka phantsi ifolokhwe yazo yasekhohlo. Kwaye kuvela ukuba batya ukutya okuninzi, ngelixa abanye beqala ukulamba, nangona imicu ibaluleke kakhulu. Apha abalambanga ngokupheleleyo, kuba. izithandi zobulumko ezimbi babeka iifolokhwe zabo emva ngamanye amaxesha. Kuvela ukuba abantu abalungileyo badla malunga namaxesha angama-5 ngaphantsi kokubi. Ngoko impazamo encinci kwikhowudi ikhokelela ekuweni kokusebenza. Kwakhona kuyafaneleka ukuba uqaphele apha ukuba imeko enqabileyo inokwenzeka xa zonke iifilosofi zithatha imfoloko ekhohlo, akukho kunene, babeka ngakwesobunxele, balinde, bathathe ekhohlo kwakhona, njl njl. Le meko ikwayindlala, ifana ne-deadlock. Ndisilele ukuyiphinda. Ngezantsi umfanekiso wemeko apho iifilosofi ezimbini ezimbi zithathe iifolokhwe zombini kunye nezimbini ezilungileyo zilambile.
Apha unokubona ukuba imisonto ivuka ngamanye amaxesha kwaye uzame ukufumana isibonelelo. Izibini ezimbini kwezine azenzi nto (igrafu eluhlaza ngasentla).
Ukufa kweSofilosofi
Ewe, enye ingxaki enokuphazamisa isidlo esizukileyo sefilosofi ukuba omnye wabo uyafa ngokukhawuleza ephethe iifolokhwe ezandleni zakhe (kwaye baya kumngcwaba ngolo hlobo). Emva koko abamelwane baya kushiywa ngaphandle kwesidlo. Unokuza nomzekelo wekhowudi yeli tyala ngokwakho, umzekelo, uphoswe ngaphandle NullReferenceException
emva kokuba isithandi sobulumko sithathe iifolokhwe. Kwaye, ngendlela, okungafaniyo akuyi kuphathwa kwaye ikhowudi yokufowuna ayiyi kuyifumana nje (kule nto AppDomain.CurrentDomain.UnhandledException
kunye njl.). Ke ngoko, abaphangi beziphoso bayafuneka kwimisonto ngokwayo kunye nokupheliswa okuhle.
Umlindi
Kulungile, siyisombulula njani le ngxaki, indlala, kunye nengxaki yokufa? Siza kuvumela ifilosofi enye kuphela ukuba ifikelele kwiifolokhwe, yongeza ukukhutshelwa ngaphandle kwemisonto kule ndawo. Ukwenza njani? Masithi kukho i-weyitala ecaleni kweentanda-bulumko enika imvume kuyo nayiphi na intanda-bulumko ukuba ithathe iifolokhwe. Siyenza njani lo mlindi kunye nendlela iifilosofi ziya kumbuza ngayo, imibuzo inomdla.
Eyona ndlela ilula kuxa iintanda-bulumko ziya kuhlala zicela umncedisi ukuba afikelele kwiifolokhwe. Ezo. ngoku iintanda-bulumko aziyi kulinda ifolokhwe ekufutshane, kodwa linda okanye ubuze umncedisi. Ekuqaleni, sisebenzisa kuphela iSithuba soMsebenzisi kule nto, kuyo asisebenzisi ukuphazamiseka ukubiza naziphi na iinkqubo ezivela kwi-kernel (malunga nazo ngezantsi).
Izisombululo kwindawo yomsebenzisi
Apha siya kwenza okufanayo njengoko sasiqhele ukwenza ngefolokhwe enye kunye neefilosofi ezimbini, siya kujikeleza kumjikelo kwaye silinde. Kodwa ngoku kuya kuba zonke iifilosofi kwaye, njengokuba kunjalo, ifolokhwe enye kuphela, i.e. kunokuthiwa kuphela isithandi sobulumko esathatha le "folokhwe yegolide" kwi-weyitara iya kudla. Kule nto sisebenzisa iSpinLock.
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
lo ngumqobo, kunye, ngokurhabaxa, into enye while(true) { if (!lock) break; }
, kodwa "ngomlingo" ngakumbi kunangaphakathi SpinWait
(esetyenziswa apho). Ngoku uyakwazi ukubala abo balindileyo, abalalise kancinane, nangaphezulu. njl. Ngokubanzi, yenza konke okusemandleni ukukhulisa. Kodwa kufuneka sikhumbule ukuba oku kusengumjikelezo ofanayo osebenzayo odla izixhobo zeprosesa kwaye ugcine ukuhamba, oku kunokukhokelela kwindlala ukuba enye yefilosofi iba yinto ephambili kunabanye, kodwa ingenayo ifolokhwe yegolide (ingxaki ye-Priority Inversion) . Ke ngoko, siyisebenzisela kuphela utshintsho olufutshane kakhulu kwimemori ekwabelwana ngayo, ngaphandle kweefowuni zomntu wesithathu, izitshixo ezifakwe kwindlwane, kunye nezinye izinto ezimangalisayo.
Umzobo we SpinLock
. Imilambo ihlala "isilwela" ifolokhwe yegolide. Kukho ukungaphumeleli - kumzobo, indawo ekhethiweyo. Ii-cores azisetyenziswanga ngokupheleleyo: kuphela malunga ne-2/3 ngale misonto emine.
Esinye isisombululo apha siya kuba kukusebenzisa kuphela Interlocked.CompareExchange
ngokulinda okufanayo okusebenzayo njengoko kubonisiwe kwikhowudi engentla (kwiintanda-bulumko ezilambileyo), kodwa oku, njengoko sele kuthethiwe, kunokukhokelela ekuthinteleni.
phezu Interlocked
Kufuneka kuqatshelwe ukuba akukho kuphela CompareExchange
, kodwa kunye nezinye iindlela zokufunda NOKUBHALA ngeathom. Kwaye ngokuphindaphinda utshintsho, xa kwenzeka enye intambo inexesha lokwenza utshintsho (funda 1, funda 2, bhala 2, bhala i-1 imbi), ingasetyenziselwa utshintsho oluyinkimbinkimbi kwixabiso elilodwa (I-Interlocked Nantoni na iphethini) .
Izisombululo zeModi yeKernel
Ukunqanda ukuchitha izibonelelo kwi-loop, makhe sibone ukuba singawuvala njani umsonto. Ngamanye amazwi, ukuqhubela phambili nomzekelo wethu, makhe sibone indlela i-weyitala elala ngayo intanda-bulumko kwaye imvuse kuphela xa kuyimfuneko. Okokuqala, makhe sijonge indlela yokwenza oku ngokusebenzisa imowudi ye-kernel yenkqubo yokusebenza. Zonke izakhiwo apho zihlala zicotha kunezo zikwindawo yomsebenzisi. Amaxesha amaninzi acothayo, umzekelo AutoResetEvent
mhlawumbi 53 amaxesha kancinci SpinLock
[Ubutyebi]. Kodwa ngoncedo lwabo, unokulungelelanisa iinkqubo kuyo yonke inkqubo, elawulwa okanye hayi.
Ulwakhiwo olusisiseko apha sisimaphore esicetyiswe nguDijkstra ngaphezulu kwesiqingatha senkulungwane eyadlulayo. I-semaphore, ngokulula, yinani elipheleleyo elichanekileyo elilawulwa yinkqubo, kunye nokusebenza kabini kuyo, ukunyusa kunye nokunciphisa. Ukuba iyasilela ukunciphisa, zero, ngoko umsonto wokufowuna uvaliwe. Xa inani linyuswa ngomnye umsonto osebenzayo / inkqubo, emva koko imisonto iyatsitywa kwaye i-semaphore iphinda icuthwe ngenani eligqithisiweyo. Omnye unokucinga ngoololiwe kwi-bottleneck ene-semaphore. .NET inikezela ngolwakhiwo oluninzi olunomsebenzi ofanayo: AutoResetEvent
, ManualResetEvent
, Mutex
kunye nam Semaphore
. Siza kusebenzisa AutoResetEvent
, le yeyona nto ilula kolu lwakhiwo: amaxabiso amabini kuphela 0 kunye ne-1 (bubuxoki, yinyaniso). Indlela Yakhe WaitOne()
ibhloka umsonto wokufowuna ukuba ixabiso belingu-0, kwaye ukuba u-1, yehlisela ku-0 kwaye iyitsibe. Indlela Set()
inyusa ukuya ku-1 kwaye ivumela iweyitala enye iphumele, ethi iphinde yehlele ku-0. Isebenza njengendawo ejika kaloliwe engaphantsi komhlaba.
Masidibanise isisombululo kwaye sisebenzise isitshixo kwisithandi sobulumko ngasinye, kwaye hayi sonke ngaxeshanye. Ezo. ngoku kunokubakho iintanda-bulumko ezininzi ngexesha elinye, hayi enye. Kodwa siphinde sithintele ukufikelela kwitafile ukuze ngokuchanekileyo, siphephe iintlanga (iimeko zobuhlanga), thatha ii-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 okwenzekayo apha, qwalasela imeko xa isazi sefilosofi asiphumelelanga ukuthatha iifolokhwe, ngoko izenzo zakhe ziya kuba ngolu hlobo lulandelayo. Ulindele ukufikelela etafileni. Akuba eyifumene, uzama ukuthabatha iifolokhwe. Ayizange isebenze. Inika ukufikelela kwitheyibhile (ukukhutshelwa ecaleni). Kwaye udlula "indlela" yakhe (AutoResetEvent
) (zivuliwe ekuqaleni). Ingena kumjikelo kwakhona, kuba akanazo iifolokhwe. Uzama ukuzithatha aze ame "kwi-turnstile" yakhe. Omnye ummelwane onethamsanqa ngasekunene okanye ngasekhohlo, ukugqiba ukutya, uvula ifilosofi yethu, "evula i-turntile yakhe." Ifilosofi yethu iyayidlula (kwaye ivala emva kwayo) okwesibini. Uzama okwesithathu ukuthatha iifolokhwe. Nqwenelela impumelelo. Kwaye udlulisa ithuba lakhe lokutya.
Xa kukho iimpazamo ezingalindelekanga kwikhowudi enjalo (zihlala zikhona), umzekelo, ummelwane uchazwe ngokungachanekanga okanye kwenziwa into efanayo. AutoResetEvent
yabo bonke (Enumerable.Repeat
), ngoko iifilosofi ziya kuba zilindele abaphuhlisi, kuba Ukufumana iimpazamo kwikhowudi enjalo ngumsebenzi onzima kakhulu. Enye ingxaki yesi sisombululo kukuba ayiqinisekisi ukuba enye ifilosofi ayiyi kulamba.
Izisombululo zeHybrid
Sijonge iindlela ezimbini zokubeka ixesha, xa sihlala kwimowudi yomsebenzisi kunye ne-loop, kwaye xa sivimba umsonto ngekernel. Indlela yokuqala ilungile kwizitshixo ezimfutshane, okwesibini ixesha elide. Ngokuqhelekileyo kuyimfuneko ukuba uqale ulinde ngokufutshane ukuba utshintsho lutshintshe kwi-loop, kwaye emva koko uvale intambo xa ukulinda kukude. Le ndlela iphunyezwa kwinto ebizwa. izakhiwo hybrid. Nazi izinto ezifanayo zokwakha njengemowudi ye-kernel, kodwa ngoku ngemowudi yomsebenzisi loop: SemaphorSlim
, ManualResetEventSlim
njl Uyilo oludumileyo apha lu Monitor
, ngokuba kwi C # kukho eyaziwayo lock
isintaksi. Monitor
le semaphore efanayo kunye nexabiso eliphezulu le-1 (mutex), kodwa ngenkxaso yokulinda kwi-loop, i-recursion, i-Condition Variable pattern (ngaphezulu kwelo lingezantsi), njl. Makhe sijonge isisombululo ngayo.
// Π‘ΠΏΡΡΡΠ΅ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ Π΄Π»Ρ ΠΠΎΠ½ΠΈΡΠΎΡΠ° ΠΎΡ Π²ΡΠ΅Ρ
, ΡΡΠΎΠ±Ρ Π±Π΅Π· Π΄Π΅Π΄Π»ΠΎΠΊΠΎΠ².
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);
}
}
Apha siphinda sivala itafile yonke ukuba ifikelele kwiifolokhwe, kodwa ngoku sivula yonke imisonto ngaxeshanye, hayi abamelwane xa umntu egqiba ukutya. Ezo. kuqala, umntu udla kwaye avale abamelwane, kwaye xa lo mntu egqiba, kodwa ufuna ukutya kwakhona ngoko nangoko, ungena ebhlokweni kwaye uvusa abamelwane bakhe, kuba. ixesha layo lokulinda lincinci.
Le yindlela esiphepha ngayo i-deadlocks kunye nendlala yesithandi sobulumko. Sisebenzisa i-loop ukulinda okufutshane kwaye sivale intambo ixesha elide. Ukuvula wonke umntu ngaxeshanye kuyacotha kunokuba ummelwane ebengavaliwe, njengakwisisombululo AutoResetEvent
, kodwa umahluko akufanele ube mkhulu, kuba imisonto kufuneka ihlale kwindlela yomsebenzisi kuqala.
Π£ lock
I-syntax inezothuso ezimbi. Ncoma ukusebenzisa Monitor
ngqo [Richter] [Eric Lippert]. Enye yazo yileyo lock
rhoqo ngaphandle Monitor
, nokuba bekukho ngaphandle, apho omnye umsonto unokutshintsha imeko yememori ekwabelwanayo. Kwiimeko ezinjalo, kudla ngokuba ngcono ukuya kwi-deadlock okanye ngandlela thile ukuphelisa inkqubo ngokukhuselekileyo. Enye into emangalisayo kukuba iMonitor isebenzisa iibhloko zongqamaniso (SyncBlock
), ezikhoyo kuzo zonke izinto. Ngoko ke, ukuba kukhethwe into engafanelekanga, unokufumana lula ukuvala (umzekelo, ukuba utshixa kumtya ongaphakathi). Sisebenzisa into ehlala ifihliwe kule nto.
Ipatheni yoKuguquguquka kwemeko ikuvumela ukuba uphumeze ngokuthe kratya ulindelo lwemeko ethile entsonkothileyo. Kwi-NET, ayiphelelanga, ngokombono wam, kuba kwithiyori, kufuneka kubekho imigca emininzi kwizinto ezahlukeneyo (njengakwiPosix Threads), kwaye hayi kwi lok enye. Emva koko umntu unokuzenzela zonke iintanda-bulumko. Kodwa nakule fomu, ikuvumela ukuba unciphise ikhowudi.
ezininzi zobulumko okanye async
/ await
Kulungile, ngoku singakwazi ukubhloka imisonto ngokufanelekileyo. Kodwa kuthekani ukuba sineentanda-bulumko ezininzi? I-100? 10000? Umzekelo, sifumene izicelo eziyi-100000 kwiseva yewebhu. Kuya kuba ngaphezulu ukwenza intambo kwisicelo ngasinye, kuba imisonto emininzi kangaka ayizukuqhuba ngaxeshanye. Iza kubaleka kuphela kangangoko kukho iicores ezisengqiqweni (ndine-4). Kwaye bonke abanye baya kuthatha nje izixhobo. Esinye isisombululo kule ngxaki yi-async / ipateni yokulinda. Ingcamango yayo kukuba umsebenzi awuwubambi umsonto ukuba ufuna ukulinda into eqhubekayo. Kwaye xa isenza into, iphinda iqalise ukuphunyezwa kwayo (kodwa hayi kumsonto ofanayo!). Kwimeko yethu, siya kulinda ifolokhwe.
SemaphoreSlim
inele nto WaitAsync()
indlela. Nantsi ukuphunyezwa usebenzisa le pateni.
// ΠΠ°ΠΏΡΡΠΊ ΡΠ°ΠΊΠΎΠΉ ΠΆΠ΅, ΠΊΠ°ΠΊ ΡΠ°Π½ΡΡΠ΅. ΠΠ΄Π΅-Π½ΠΈΠ±ΡΠ΄Ρ Π² ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ΅:
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
iguqulelwe kumatshini welizwe olukhohlisayo olubuyisela ngokukhawuleza lwangaphakathi Task
. Ngayo, unokulinda ukugqitywa kwendlela, uyirhoxise, nayo yonke enye into onokuyenza ngoMsebenzi. Ngaphakathi kwendlela, umatshini wombuso ulawula ukubulawa. Eyona nto iphambili kukuba ukuba akukho kulibaziseka, ngoko ukuphunyezwa ku-synchronous, kwaye ukuba kukho, ngoko intambo iyakhululwa. Ukuqonda ngcono oku, kungcono ukujonga lo matshini karhulumente. Unokwenza amatyathanga kwezi async
/ await
iindlela.
Masivavanye. Umsebenzi weefilosofi ezili-100 kumatshini onama-4 cores anengqondo, imizuzwana eyi-8. Isisombululo sangaphambili kunye neMonitor siqhube kuphela imisonto emi-4 yokuqala kwaye yonke eminye ayizange ibaleke kwaphela. Ngamnye kule misonto mi-4 yayingasebenzi malunga ne-2ms. Kwaye isisombululo se-async / sokulinda siqhube zonke ii-100, kunye nokulinda okuphakathi kwe-6.8 imizuzwana nganye. Ewe, kwiinkqubo zokwenyani, ukungenzi nto kwimizuzwana emi-6 akwamkelekanga kwaye kungcono ukuba ungaqhubeki nezicelo ezininzi ezinje. Isisombululo esineMonitor siye sabonakala singenabungozi kwaphela.
isiphelo
Njengoko unokubona kule mizekelo mincinci, .NET ixhasa ungqamaniso olwakhayo oluninzi. Nangona kunjalo, akusoloko kubonakala indlela yokuzisebenzisa. Ndiyathemba ukuba eli nqaku libe luncedo. Okwangoku, oku kukuphela, kodwa kusekho izinto ezininzi ezinomdla ezisele, umzekelo, ukuqokelelwa kwentambo, i-TPL Dataflow, i-Reactive programming, imodeli ye-Software Transaction, njl.
Imithombo
- Umbono wokuqukuqela:
Concurrency Visualizer - MSDN:
zomsonto ,Iipateni zenkqubo ezingahambelaniyo nabanye abaninzi nezinye - [Richter] - CLR nge-C #, uJeffrey Richter
- [uEric Lippert] -
Malunga nesitshixo Ikhowudi yomthombo - Umfanekiso - "Danisa phakathi kwamakrele", G. Semiradsky
umthombo: www.habr.com