ášáááµááᜠášáááá¢á« áœááá á¥áá° áá³á á áá áá á ááµ áá ášá°á£á£á á¥á áµáá© áá®áá«ááá á¥ááŽáµ á¥áá°áá°á« á¥ááᢠá¥á á± áá áá, áá®áœ / áá°á¶áœá ášááá³á°á, áá° á°ááá ááŽá (á áášá°ááµ ááááœ). áœáá áááááªá«á áµááá ááá á¥áááµáá ááá°áµ á áá ááá ááœááá¢
ááááµáá á áá«áœ? áµá«áááµá°á®áœ á áµáá¹ áá áážá áá áá°áá³á, ášáá á á á á¥ááá áá¥ááµ áá°á¥ áá ášá°áá°ášá° áá, áµááá á á áá¥á áášáá áá³á«á, á¥á áµá«áááµá°á®áœ áá°ášá ááœáá. á á°áá³á³á áá ášááᥠáá á á¥á«á°á áá, á¥á á°á áááᜠášáµáá á¶áœ á áá£á ááᜠáá á¥áá. á á¥áá°áá ááááµ ááá³, "ášá°ááá°" áá®áá«á, á ááµ ášááµáážáá« áá á²áášá, ášá áá á áá áá€á³á á áá°áá. á á ááµ áá ááá á á ááµ áá ášá áááá áœááá á áá ááááµ ááá³áµ á«áµááááá³áᢠášáá á á áá áá áœáá á á°áá«á© á°ášááᜠááµá¥ áááá: á áá®áœ á°ášá, á áá°áµ á°ášá, á ááµááá ááµá¥ á£á ááœáᜠ(ášá°ášááá áµááá¶áœ). .NET á¥áá°áá á áááµ áœáá®áœá á áá¥ááµ á¥á á á¥ááµ áááá³áµ ášáá°á á¥á«áµ á«áážáᣠá áá ášá°áášá© áŽáááááᜠá ááµá¢
ááá
Edsger Dijkstra áá áá áœáá áá°ááªáá¹ á¥áá° 1965 á á áá§áᢠášá°áááá ááá á¥áá°áášá°áá ááᢠášá°áá°á (á¥ááá áá á ááµáµ) ášáááµáᜠáá¥á á¥á á°áá³á³á ášá¹á«áᜠá¥ááµ á áᢠáᥠá ášáŽá áá á°ááá áá, á áá«ášáážá á¹á«ááœ. áááµááᜠáááá« ášáááá ááᥠášá³á áážá áá¥ááµá£ áá°á¥ ááá áá á á ááœááᢠáááµáá ááá¥ááµ, áááµ á¹á«ááœá ááá°áµ á«áµááááá³á (ášáášášá»á á¹á«áá ášááááªá«á áá ááá«á). á¹á« ááá³áµ á¥á ááµááᥠáááµ ášá°áá«á© á°áá£á«áµ áážáᢠááá áááµáᜠáá á áᢠáµá«á ááá áš 54 á áá³áµ á áá á¥áá³á ášáá«áµá¡ á¥á ášáááá áµ á¥áá²á á áááµ áµáá°-ááá ááááµ áá.
á ááááªá«, áá áá áœáá á áá« áŠá³ á áá áá áááá³áµ á¥áááá. á¹á«áá¹ á áá« á ášáŽá áá áá°áá á¥á áááµááá¹ á ááá á²áá ááµá°á áááá³á. á¥áá á ááá³á°á áá áœáá®áœ á áᣠáጠá áµááá surebets ááá°áµ? á¹á« ášáááµ? ááá° áá ááááªá« áááµááá¹á á¥ááááá¢
áá®áœ ááááá, á áá ááá³ á¥áá áááá Task.Run
ááŽá¡
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);
}
ášáá ááá³á ášá°áá°áá áá ááá á á¥á áá°ášáá áááá»ážáµ ááᢠáá
ááá³ ášá°áá£á®áœ áá áášá á áá á¥á CLR á á¥ááá
áµá«áᜠá¥ááµ áá á áááµášáµ áá®áœá ááá¥á«á ááá á«áµááá³áᢠáááá AppDomains á ááµ ááá³á¢ áá
ááá³ áááá áááµ áá»áá á¥á
á áá ááá á áá áµ, áááá«á±á. áá®áœá á ááá á ᣠá áá°ášá ᣠáášááá»ážáá ᣠááá° ášáá³á°ááµá áášáá
á á«áµáááá ᢠá«á ááá³ áá»áá ᣠáá ášáá« á áá¥á³ áá áá á áá¥ááµá¢ Thread
, áá
ášááá á
áµáá« áááᥠá ááááá áµ áá, ášá¥á áᶠá¥áá á²áášá, ááášáá«á áá, ááá° ááá³á®áœ á áá áá.
á áá áá, System.Threading.Tasks.Task
ááá á ááµ áá Thread
, ááá áá ášááá á áááµ ááŸáµ áá-ášááᜠá°áá£á«áµ á¥áá³ á áá á ááµá á°áá£á ášáááµ áœáá³, ášá°áá£á®áœ ááááµ, áá¹ á áá ááá³ ááášá¥ á¥á áááœáá. ááá° ášáá á©áµá ááá£á³áᜠ(á á°áá£á áá ášá°áá°ášá° Asynchronous Pattern, syntactic sugar for IO áµá«ááœá ááá á á
) ááá°áá á«áµáááá. áµááá
áá³á á áá á¥ááááá«áá.
CancelationTokenSource
áá© á¥á«á±á á á¥áªá áá ááááµ áá á¥áá²á«áá á¥áá
á«áµáááá.
ášááá³á°á áá³á®áœ
ášá³áá± áááµááœ
á¥áºá£ áá®áœ á¥ááŽáµ á¥áá°ááá á© á¥áááááᣠáá³ ááá¥ááµ á¥ááááá¡-
// ÐÑП какОе вОлкО взÑл. РпÑОЌеÑÑ: 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();
}
}
á¥áá
á ááááªá« ášáá«áá á¹á«, á¥á áµáááááá á¹á« áááá°áµ á¥áááá«áá, á¥á ášá°á°á«, á¥áá ááá á¥á á¥áááá³ážááá. á ááµ á¹á« ááá°áµ á á¶áá áá, áááµá. áááµ áá®áœ á á ááµ áá á ááµ áá ááá°áµ á ááœáá (áµááá á áá°áá: ášááááªá«á á¹á«á áá á¥áá°áá ááá á£á, ááá°áá - á á£á, ášááááªá«á áááµá³á, ááá°áá áááµá³á). ááá
Interlocked.CompareExchange
á á ááá£á£áª áááªá« áá°áá á á«áá áµ (TSL
, XCHG
)ᣠáá á¶áá á°ášá³á³á áá£á¥ á¥á ááá áµááµá³á ášáááááᢠá¥á SpinWait ášááá£á³á áá á¥á©á ááᢠwhile(true)
á áµáᜠ"á áµááµ" á¥á» - áá© áááá£á áªá«áá áááµá³á (Thread.SpinWait
ááá áá á áá³ááµ áá ááá£á áªá«á áá° áá áá á«áµá°áááá (Thread.Yeild
ááá á¥áá
áá áá°ááµ (Thread.Sleep
).
áá áá áááµá á áá°á«á, áááá«á±á áá°á¶á¹ á á áá¡ (áá¥á á á ááµ á°ášááµ ááµá¥) á³áá°ááá¡ ááá áááµáᜠášáá« á¹á«ážáá áááµá³á, áá áµáááááá á áá°áá. ášá¹á«áá¹ áµááµá á¥áŽá¶á¹ á ááµá¡ 1 2 3 4 5á¢
á á¥áá áá, ášááá áá®áœ (deadlock). á ášááᎠ- ááµáážáá«, áá - ááá³á°á, áá«á« - áá á°áá·á. á«áá¡á¶áœ ášá°áá£á ááááªá« ááá á«áááá³áá¢
ášáááµáᜠášáá¥
ááá á¥áá³á á á°áá á¥á ááá¥á áá°á¥ á áµááá á£áááá ášáᥠáá ááá á°á áááµááá á¥áá²á°á á«á°áááá. á áœáá«áœá ááµá¥ áá®áœ ášá°á«á¡á áµá ááá³ áááá°á á¥áááá. ášáᥠáááµ áá á²á®á¥ áá, ááá áá ááá ášáá áµá« ášáá, á áá á áááá, áá á°áá³á³á ášááµ ááááá« áá, á áá áá© á áá°áá, ááá áá ášáá á áááá á áááµ á¥ášááá áá, ááá áá ááá ááᥠášáá. á°á°ááá á¥áá³á áááµáášáµ, áá ááá°áµ á«áá»áá á¹á«áá á¥ááµááá ááá.
// ТП же ÑÑП О в 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)
);
}
á áá á®áµ ááµá¥ ááá ááá ášá á«á± áááµááᜠááá± ášáá« á¹á«ážáá ááµááᥠáášá³á. á¥á á°ášá᪠ááᥠá¥áá°áááá¡, ááᜠá°áá á ášáᥠáááá«á, ááá á¥áá³á áá®áœ á°áá³á³á á áµáá« á¢áá«ážáá. á¥áá áá á áá ášá°á«á¡ á áá°áá, áááá«á±á. áá¥á áááµáᜠá áá³ááµ áá á¹á«áá»ážáá áá° áá áááá³á. á¥á© á°áᜠášáá¥á 5 á¥á¥á á«áá° áá áá. áµááá á á®á± ááµá¥ áµáᜠáµá á°áµ áá° á áááá ááµááµ ááá«á. á¥áá áá á°áá ááá áááµáᜠášáá«áá á¹á« á²ááµá±á£ áá ášááᣠáá«áá á²á«áµááá¡á£ á²á á¥áᣠáá«áá á¥áá°áá á²ááµá±á£ ááá° á«áá°ááá° ááá³ áášá°áµ á¥áá°ááœá áᥠáá£á ááá£áᢠáá ááá³ á°áá ášáᥠáá, ášá áá á¥áá° áááµ. ááµááá á áá°áᢠášá³áœ á«áá ááµá áááµ áá¥á áááµáᜠááá±áá á¹á« ášáá°á±á áµ á¥á áááµ á¥á©áᜠá ášáᥠášá°á áá áµ ááá³ áá.
á¥áá áá®á¹ á áá³ááµ áá ášá¥áá áá á¥áá°ááá á¥á áá¥á±á áááááµ á¥áá°áááá© áášáµ ááœáá. ášá á«á± á®á ááá± ááá á á«á°ááá (ášáá á ášááᎠáá«á)á¢
ášáááµá ááµ
á¥ááá²á
ᣠášáááµááœá ášášá áš á¥á«áµ áá«ááá á ášááœáá ááá áœáá á áá± á áµáááµ á á¥á á¹á« áá á¢ááµ (á¥áá°áá«á ááá¥á©á³á)ᢠášáá«á áášá€á¶áœ á«á áá³ ááá«á. ááá
áá³á á¥á«áµá áá³á á®áµ ááá ááá£áµ ááœáá, ááá³á, á°á¥áá NullReferenceException
áááµáá á¹á«áá¹á ášáá°á° á áá. á¥á ᣠá ááá«áœá áá ᣠáá©áá± á ááµá°áááµá á¥á ášá¥áª á®áµ á¥á» á ááááá (ááá
AppDomain.CurrentDomain.UnhandledException
á¥á ááá°)ᢠáµááá
ášáµá
á°áµ á°áá£á£áªáᜠá á«á³ážá áá®áœ ááµá¥ á¥á á áá«áá ááášá« á«áµááááá¢
á áµá°ááá
á¥áºá£ áá áá ášááááµá£ ášášáᥠá¥á ášááµ áœáá á¥ááŽáµ á¥ááá³ááá? á¹á«áá¹ áá á ááµ áááµá á¥á» á¥áá²á°ááµ á¥ááá á³áá á£ááá áŠá³ áá®áœ ášáá« ááááá á¥áášááᢠá¥ááŽáµ ááµášá áá»áá? á ááµ á áµá°ááá á¹á«áá¹á á¥áá²ááµáµ áááááá áááµá áááµ ášá°á á áááµááá¹ á á áᥠáá á¥áá áᢠáá áá á áááá á¥ááŽáµ á¥áá°ááá°ááá á¥á ââáááµááᜠá¥ááŽáµ á¥áá°áá áááµ, á¥á«ááá¹ á áµá°á³áœ áážá.
á á£á ááá ááááµ áááµááá¹ á ááá á¹á«áá¹ á¥áá²á°áá±á áµ á áµá°áááá áááá á²á áá ááᢠá¥ááá«á¢ á áá áááµááᜠá á á á«á¢á«á á«ááá á¹á« á áá á¥áá ᣠáá áá á¥á ááá á áµá°áááá áá ááᢠááááªá« áá ááá ášá°á áá áŠá³á á¥á» á¥áá áááá ᣠá á¥á± ááµá¥ áááááá áá°á¶áœ ášášááá ááá°áá ááášá¥á á áá ááá (ášáá á á³áœ áµáá¥áá±)á¢
á á°á áá áŠá³ ááµá¥ áááµáááœ
á¥áá áá á á ááµ á¹á« á¥á áááµ áááµááᜠá¥áá°ááá°ááá á°áá³á³á ááá á¥áá°áááá, á áá°áµ ááµá¥ á¥ááœášášáá«áá á¥á á¥áá á¥ááá. á áá áá ááá áááµáᜠá¥á á¥áá°áá á©, á ááµ á¹á« á¥á» áááá, áááµá. áá á "áááá á¹á«" ášá áµá°ááá ášáá°á°á áááµá á¥á» áá áá áááµ áá»áá. ááá á 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
áá
ááá ááᣠá áááµ á²áááᣠá ááµ ááᢠwhile(true) { if (!lock) break; }
ááá áá ášááµá¥ ášá áá "á áµááµ" á«áá SpinWait
(á¥áá« á¥á
á áá ášáááá). á áá ášáá á£á ááµá á¥ááŽáµ á¥áá°ááá¥áᣠáµáᜠá¥áá²á°á á¥á áááœáá á«áááᢠááá° á á á ááá, áááá»ážáµ ášá°á»ááá áá á«á°ááá. ááá áá áá
á ááá á«á ášá ááá£á£áª áá¥á¶áœá ášáá á á¥á áá°á±á ášáá á¥á
á«á áá áá°áµ áááá ááµá³ááµ á áá¥á áá
á ášáááµááá¹ á áá± ášááᜠášá áá á
áµáá« ášáá°á á ášáá áá áááá á¹á« (á
áµáá« ááá ᣠáœáá) ášááá ášáá¥á á«áµášáµááᢠ. áµááá
á¥á ášááá ááá ááá« áá
á°áš áµááµá³ á á£á á áá áááŠáœ á¥á» ááᣠá«á ááá ášá¶áµá°á ááá á¥áªá£ ášáá ááááá« á¥á ááᜠá áµáá«á ááá®áœá¢
á¥áá á SpinLock
. á
ášá¶áœ áááááá á¹á« á«áááášá¥ "áááá". ááµáá¶áœ á á - á á¥áá áá, ášá°áášá á áŠá³. á®áá¶á¹ áá á áá á¥á
á áá á áááá: á á¥ááá
á á«áµ áá®áœ 2/3 á«á
á á¥á»á¢
á¥áá
áá áááµá á¥á» áá áá áááá Interlocked.CompareExchange
ášáá á£áá á®áµ (á á°á«á¡ áááµáááœ) áá á¥áá°áá³ášá á á°áá³á³á áá á¥á á ᣠáá áá
ᣠáá°á á²á á¥áá°á°ááášá ᣠá ááµá-áá³á¥ áá° áááµ áá«áá« ááœááá¢
áá Interlocked
á¥á» á¥áá³ááá áᥠáá£á ááá£áᢠCompareExchange
ᣠáá áá á¶áá áá£á¥ á¥á áááá ááᜠááŽááœá¢ á¥á á ááá¡ áá°ááá á£áá áá áááŠá¹á áááµášá áá á¢áášá (1 á áá¥á¥ ᣠ2 á áá¥á¥ ᣠ2 áá ᣠ1 áá áá¥á áá) áá ááµ á¥áŽáµ ááµá¥áµá¥ áááŠáœá áá áá áá»áá (ášá°á ááá ááááá ááá ááµá) .
ášášááá ááá³ áááµáááœ
á loop ááµá¥ áá¥áµá áááá£ášáᣠááá á¥ááŽáµ á¥áá°ááááµá¥ á¥ááᢠá áá á ááááᣠáá³áá áœáá á ááá áᣠá áµá°ááá áááµááá á¥ááŽáµ á¥áá°áá«áááá á¥á á áµááá á²áá á¥á» á¥áá°áá«áµáá³á á¥áááášáµá¢ á ááááªá« ᣠáá
áá á áµááá° áááá ášášááá ááá³ á¥ááŽáµ á¥áá°ááá°áá á¥áááášáµ ᢠá¥áá« á«ááµ ááá ááá
á®áœ á á°á áá áŠá³ ááµá¥ á«ááµ ááá
áááá áážáᢠá¥á áá ááááᣠááá³á AutoResetEvent
áááá£áµ 53 áá áááá SpinLock
[áªáœá°á]ᢠááá áá á á¥áá± á¥áá³á³ á áµááá± ááµá¥ á«ááµá áá°á¶áœ ááá³á°á ááœáá, ášáá°á³á°áá áá á áá°áá.
á¥áá
á«áá áá ášá³á ááá£á³ ášááᜠááá° áááµ á ááµ á á²á
ááµá³á« ášáášá á áŽááá ááᢠáŽááá á ááá á áááá á áµááá± ášáá°á³á°á á ááá³á á¢áá²áá á¥á á áá© áá áááµ ááááᜠáášáá á¥á ááááµ áážáᢠááááµ á«áá°á³á«, áá®, ášáá«á ášá¥áªá áá á³áá·á. áá¥á© á áá áá£áª áá/áá°áµ á²ášáá áá©á¹ á°áááá á¥á áŽááá© á¥áá°áá á á°áááá áá¥á áááá³áᢠá£á¡á®áœá á á áááµ á áááµ ášáŽááá áá ááááµ áá»ááᢠ.NET á°áá³á³á á°áá£á á«áážá á áá«á³ ááá£á³ááœá á«ááá£áá¡- AutoResetEvent
, ManualResetEvent
, Mutex
á¥á á¥á á«áŽ Semaphore
. á¥áá áááá AutoResetEvent
áá
ášá¥ááá
ááá£á³áᜠááµá¥ á á£á ááá áá-áááµ á¥áŽá¶áœ 0 á¥á 1 (áážáµ ᣠá¥áááµ) á¥á»á¢ ášá¥á· áᎠWaitOne()
á¥áŽá± 0 ášáá ášá¥áªáá áá á«áá³áᣠá¥á 1 ášááᣠáá° 0 áá
á áµááá áááááᢠáᎠSet()
áá° 1 ášá á¥á á ááµ á áµá°ááá
á¥áá²á«áá á«áµáœáááᣠá¥á±á á¥áá°áá áá° 0 áá
á¥áᢠá¥áá° ášááµá ááµá¥ á£á¡á áá³á áá« ááá¢
áááµááá á¥ááá³áµá á á¥á ááááá«áá áá¥á«áá³áá± áááµá á¥áá áá á¥áá áááá á á ááµ áá á áá°ááᢠá¥ááá«á¢ á áá á¥á áááµáᜠá á ááµ áá ááá© ááœáá, á¥á á ááµ á áá°áá. ááá áá ášáá (ášáá ááá³ááœ) áá«á áᣠá¥ááá á áááá ášá áá ášá¡á áá³ášá» á¥áá°áá á¥áášáááááá¢
// ÐÐ»Ñ Ð±Ð»ÐŸÐºÐžÑÐŸÐ²Ð°ÐœÐžÑ ÐŸÑЎелÑМПгП ÑОлПÑПÑа.
// ÐМОÑОалОзОÑÑеÑÑÑ: 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();
}
á¥áá
áá á¥ášá°áá áš á¥áá³á ááášá³áµ áááµáá á¹á«ááœá ááá°áµ á²á«á
á°á áá³á©á á áµá¡á áµ, ášáá«á áµááá± á¥áá°áášá°áá áááá. áá° á ášáŽáá áááµášáµ á¥ášá á á áá. ášá°áá á á áá á¹á«áá¹á áááá°áµ áááá«á. á áá°á«áᢠáá° á ášáŽáá (ášáá« áááá) áá³ášá» áá°á£á. á¥á ášá¥á±á "áááªá«" á«ááá (AutoResetEvent
) (á ááááªá« áááµ áážá). á¥áá°áá áá° áá°áµ ááµá¥ ááá£á, áááá«á±á á¹á«áᜠášááµáᢠáááµá³ážá áááá«á á¥á á á¥á± "áá³á áá«" áá áááá. á ááá á áá«á á áá³ááµ á¥áµáá áášá€á¶áœ á áá°á á¥áá°ášášá± áááµááœáá âáá³á áá«áá ášáá¶â ášáá°áá³áᢠáááµááœá á ááá (ášáááá áááá) áááá°á ááᢠá¹á«áá¹á áááá°áµ ááŠáµá°á áá áááá«áᢠááá«á áááµ. á¥á ááááᥠáááªá«áá á«áááá¢
á á¥áá°áá
ááááµ á®áµ ááµá¥ ášáááá° áµá
á°á¶áœ á²áá© (ááááá ááá«á) ááá³á áášá€áµ á áµá
á°áµ áááá»á ááá á°áá³á³á ááá ááá á«áᢠAutoResetEvent
áááá (Enumerable.Repeat
), ášáá« áááµááá¹ ááá¢ááœá áá á¥áá, áááá«á±á á á¥áá°áá
ááááµ á®áµ ááµá¥ áµá
á°á¶áœá áááá á á£á ášá£áµ áµá« ááᢠááá ášáá
áááµá áœáá á áá³ááµ áááµááᜠá¥áá³áá«á¡ ááµáµá á áááµá á± ááá¢
áµá¥áá áááµáááœ
á á°á áá ááá³ á¥á áá ááµá¥ áµááá á¥á á ášááá ááµá¥ áá áµááá áááµ á áá«ášáŠáœá á°áááá°ááᢠášááááªá«á áᎠáá áá ááááá«áᜠá¥á© áá, ááá°áá á°áá áášá
á áá áá. á¥ááá áá á ááááªá« á ááµ á°áááá á loop ááµá¥ á¥áµáªáášá áµášáµ áá áá áá áá á á
á¥á á¥á áá ášá
á á áááá áµ áá áááá áááµ á«áµááááᢠáá
á áá«ášá¥ á áá£ááµ ááµá¥ á°á°áá¥á¯á. áµá¥áá
ááá
á®áœ. ášášááá ááá³ áá á°áá³á³á ááá£á³áᜠá¥áá
á áᣠá áá áá á á°á áá ááá³ áá°áµáŠ SemaphorSlim
, ManualResetEventSlim
ááá° á¥áá
á á£á á³ááá ááµá áá Monitor
, áááá«á±á á C # ááµá¥ á á£á ášá³áá ááá á á lock
á áá£á¥á¢ Monitor
áá
ášáá°áá 1 (mutex) á«áá á°áá³á³á áŽááá ááᣠááá áá á loop ááµá¥ ááá á á
áµáá ᣠá°á°ááááᵠᣠášááá³ á°áááá ááµá (ášáá
á á³áœ ášá áá ) ᣠááá°. ášá¥á± áá á ááµ áááµá á¥ááá¢
// СпÑÑÑеЌ ПбÑÐµÐºÑ ÐŽÐ»Ñ ÐПМОÑПÑа ÐŸÑ Ð²ÑеÑ
, ÑÑÐŸÐ±Ñ Ð±ÐµÐ· ЎеЎлПкПв.
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);
}
}
á¥áá á¥áá°áá áá° á¹á«áᜠáááµášáµ áááá á ášáŽá á¥ášááá áá, á áá áá áááá áá®áœ á á ááµ áá á¥áášáá³áá, á¥á á ááµ á°á á áᶠá²ášááµ áášá€á¶áœ á áá°áá. á¥ááá«á¢ á ááááªá« á ááµ á°á áá áá á¥á áášá€á¶á¹á á«áá³á, á¥á áá á°á á²ášááµ, ááá áá á¥áá°áá áá¥ááµ á²ááá, áá° áááµ áᶠáášá€á¶á¹á á«áµáá³á, áááá«á±á. ášá¥á± á¥á á áá á«áá° áá.
á áá
ááááµ áá ášááµ ááááµ á¥á ášá áá³ááµ áááµáááœá ášáᥠášáááµááá°áᢠáá áá áá ááá á á
á ááµ loop á¥áá áááá á¥á áááá áášá
á áá á¥áááááá. áááá á°á á á ááµ áá ááá³áµ áášá€áµ á¥á» ášáá³ááµ ááá
áááá ááᣠá¥áá° áááµáá AutoResetEvent
, áá áá©áá± áµáá
ááá ášáá áµá, áááá«á±á áá®áœ ááááªá« á á°á áá ááá³ ááµá¥ ááášáµ á áá£ážáá¢
У lock
á áá£á¥ á áµáá«á á áµáá«á ááá®áœ á ááµá¢ ááá áá ááášá«á Monitor
á áá¥á³ [Richter] [Eric Lippert]. ášáá«ášáážá á áá± á« ááᢠlock
áááá á᪠Monitor
ááá á¥áá³á ášá°ááš ááá á¢ááá ᣠá áá
ááá³ ááµá¥ áá áá ášáá« áá
á°áš áµááµá³ ááá³á áááᥠááœááᢠá á¥áá°áá
ááááµ ááá³áᜠá¥ááá áá áá° ááááá« áááµ ááá á áá ááááµ áá®áá«áá ááášá¥ áá»ááᢠááá ášáá«áµá°ááá ááá°á ášááá³á°á á¥áá®áœá áá ááá (SyncBlock
), á ááá ááá®áœ ááµá¥ ášááááµ. áµááá
ᣠá áá£á¥ á«ááá ááá ášá°áášá á ááá ááááá« (ááá³áᣠášá°á ááá áááµ áá ášááá) á ááá ááááµ ááœááᢠááá
áááá ášá°á°á ááá ááá á¥áá áááá.
ášááá³ á°áááá áµááá°-á¥ááµ ášá áá³ááµ ááµá¥áµá¥ ááá³ááœá áá á á á áµááá á¥áá²á°áá¥á© á«áµáœááá³áᢠá NET ááµá¥, á á¥á á áµá°á«ášáµ, á«áá°áá áá, áááá«á±á á ááµá áá³á¥ ᣠá á¥á á°áááá®áœ áá á¥á áášááᜠááá© ááá£á (á¥áá° ááááµ áá®áœ) ᣠá¥á á á ááµ áá ááᢠášáá«á á ááµ á°á áááá áááµáᜠáá«á°ááážá ááœáá. ááá áá á áá á ᜠááµá¥ á¥áá³á, á®á±á á¥áá²ááá± á«áµáœááá³á.
á¥á áááµáᜠááá async
/ await
á¥áºá£ á áá áá®áœ á á¥ááµ áááµ á¥ááœáááᢠáá á¥á áááµáᜠá¢áá©á? 100? 10000? ááá³áᣠááµá á áááá 100000 á¥á«ááᜠá°áá°áááᢠáá¥á«áá³áá± á¥á«á áá áááá á ášáá áááá, áááá«á±á á¥á áá®áœ á áµáá© á ááá±áᢠá áááá®á á á®á®áœ á¥áá³á á«á á á¥á» áá ášáá°á«á (4 á áá)ᢠá¥á ááá á°á áá¥áµá á¥á» áááµá³áᢠááá áœáá á áá± áááµá ášááá³á°á/ášáá á£á á á¥ááµ ááᢠášá¥á± áá³á¥ á ááµ ááá á¥áá²áá¥á áá á á á«áµááá á°áá£á© áá á áááá. á¥á á ááµ ááá á²á«á°áá á ááááá ááá¥áá (ááá áá ášááµ á á°áá³á³á áá áá á áá°áá!) á á¥á ááá³, á¹á«áá á¥áá á¥ááá.
SemaphoreSlim
ááá
á áá WaitAsync()
ááŽ. áá
áá áµááá°-á¥ááµ á áá áá áµáá á« á¥áá
á áá¢
// ÐапÑÑк ÑакПй же, как ÑаМÑÑе. ÐЎе-МОбÑÐŽÑ Ð² пÑПгÑаЌЌе:
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();
}
áᎠᚠasync
/ await
áá²á«áá ááµá£ááá áá° ááááµ á áµážá᪠ááá³ ááœá á°á°áááá Task
. á á¥á± á á©á, ááŽá á¥áµáªá ááá
áµášáµ, áá°ášá á¥á á á°áá£á ááµášá ášááœááµá áááá ááá áá á á
ááœáá. á áµáá± ááµá¥ ášáµáŽá± ááœá á ááááá ááá£á á«á. ááá ááá ááá áááášáµ ášáá, á áááá á°áá³á³á áá, á¥á á«á, ášáá«á áá ááááá. ááá
ášá°á»á áááဠáá
áá ášáµáŽáµ ááœá áááášá± ášá°á»á áá. ášá¥ááá
ááµá¥ á°áá°áá¶áœá ááá á ááœáá async
/ await
ááŽááœ.
á¥áµá² á¥áááµáœá¢ áš100 áááµáᜠáµá« 4 ááá«á á®áᣠ8 á°ášááµ á£áá ááœá ááᢠášMonitor áá ášáá ášá ášáá°áá áááµá ášááááªá«áá¹á 4 áá®áœ á¥á» áá ášáá°á á¥á ášá°áášá ááá á ááá°áᢠá¥á«áá³áá³ážá á¥ááá 4 áá®áœ á 2 áá° á«á á áµá« ááµá°ááᢠá¥á ášá áµáá/ášáá á£á á áááµáá 100á±á á®áŠ áá áá£á á¥á«áá³áá³ážá á á áá«á 6.8 á°ášááµ á¥ášá á áᢠá¥ááᥠáá, á á¥ááá°á áµááá¶áœ ááµá¥, á 6 á°ášááµ áµá« ááµááµ á°áá£áááµ ášááá á¥á á¥áá°áá á áááµ á¥á á¥á«áááœá ááááážá ášá°á»á áá. ášMonitor áá á«áá áááµá ášáᶠáá°á ášáááœá áá á°ááá·áá¢
áá°áá°áá«
ášá¥ááá áµááᜠáá³ááᜠáášáµ á¥áá°ááµáœááᣠNET á¥á ášááá³á°á ááá£á³ááœá áá°áááᢠááá áá, á¥ááŽáµ á¥áá°áá ááá£ážá áááá ááᜠá áá°áá. áá áœáá á áá áá á á¥á¬ á°áµá á á°ááááᢠáá ááᣠáá áášášá»á ááᣠááá áá á ááá á¥á á áµá°á³áœ ááá®áœ ááá°ááᣠááá³áᣠáá-á áµá°ááá áµá¥áµáŠáœá£ TPL DataflowᣠReactive programmingᣠášá¶ááµáá áá¥ááµ ááŽáᣠááá°á¢
ááá®áœ
- ášáá°áµ á¥áá³
Concurrency Visualizer - MSDN
á°ášá³á³á ,á«áá°áá³á°á ášáá®áá«á ááµáᜠá¥á áááœá ááᢠ- [áªáœá°á] - CLR á C # á á©á ᣠáá᪠áªáœá°á
- (á€áªá ááááµ) -
áµá ááááá« ááá á®áµ - áµáá - "á á°áá áá«ášá á³ááµ", á áŽáá«áµáµáª
ááá: hab.com