ํ’๋ถ€ํ•œ ์ฒ ํ•™์ž ๋˜๋Š” ๊ฒฝ์Ÿ๋ ฅ ์žˆ๋Š” .NET ํ”„๋กœ๊ทธ๋ž˜๋ฐ

ํ’๋ถ€ํ•œ ์ฒ ํ•™์ž ๋˜๋Š” ๊ฒฝ์Ÿ๋ ฅ ์žˆ๋Š” .NET ํ”„๋กœ๊ทธ๋ž˜๋ฐ

Philosophers Dining Problem์„ ์˜ˆ๋กœ ๋“ค์–ด .Net์—์„œ ๋™์‹œ ๋ฐ ๋ณ‘๋ ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ณ„ํš์€ ์Šค๋ ˆ๋“œ/ํ”„๋กœ์„ธ์Šค์˜ ๋™๊ธฐํ™”์—์„œ ํ–‰์œ„์ž ๋ชจ๋ธ(๋‹ค์Œ ๋ถ€๋ถ„์—์„œ)๊นŒ์ง€์ž…๋‹ˆ๋‹ค. ์ด ๊ธฐ์‚ฌ๋Š” ์ฒ˜์Œ ์•„๋Š” ์‚ฌ๋žŒ์ด๋‚˜ ์ง€์‹์„ ์ƒˆ๋กœ ๊ณ ์น˜๋Š” ๋ฐ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์™œ ๊ทธ๋ ‡๊ฒŒํ•ฉ๋‹ˆ๊นŒ? ํŠธ๋žœ์ง€์Šคํ„ฐ๋Š” ์ตœ์†Œ ํฌ๊ธฐ์— ๋„๋‹ฌํ•˜๊ณ  ๋ฌด์–ด์˜ ๋ฒ•์น™์€ ๋น›์˜ ์†๋„ ์ œํ•œ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋ฏ€๋กœ ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด ๋” ๋งŽ์€ ํŠธ๋žœ์ง€์Šคํ„ฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— ๋ฐ์ดํ„ฐ์˜ ์–‘์ด ์ฆ๊ฐ€ํ•˜๊ณ  ์‚ฌ์šฉ์ž๋Š” ์‹œ์Šคํ…œ์—์„œ ์ฆ‰๊ฐ์ ์ธ ์‘๋‹ต์„ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ ํ•˜๋‚˜์˜ ์‹คํ–‰ ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ๋Š” "์ •์ƒ์ ์ธ" ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๋” ์ด์ƒ ํšจ๊ณผ์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ๋“  ๋™์‹œ ๋˜๋Š” ๋™์‹œ ์‹คํ–‰ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”์šฑ์ด ์ด ๋ฌธ์ œ๋Š” ์Šค๋ ˆ๋“œ ์ˆ˜์ค€, ํ”„๋กœ์„ธ์Šค ์ˆ˜์ค€, ๋„คํŠธ์›Œํฌ(๋ถ„์‚ฐ ์‹œ์Šคํ…œ)์˜ ๊ธฐ๊ณ„ ์ˆ˜์ค€ ๋“ฑ ๋‹ค์–‘ํ•œ ์ˆ˜์ค€์—์„œ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. .NET์—๋Š” ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๊ณ ํ’ˆ์งˆ์˜ ์˜ค๋žœ ํ…Œ์ŠคํŠธ๋ฅผ ๊ฑฐ์นœ ๊ธฐ์ˆ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํƒœ์Šคํฌ

Edsger Dijkstra๋Š” ์ด๋ฏธ 1965๋…„์— ๊ทธ์˜ ํ•™์ƒ๋“ค์—๊ฒŒ ์ด ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค. ํ™•๋ฆฝ๋œ ๊ณต์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํŠน์ •(๋ณดํ†ต 54๋ช…) ์ˆ˜์˜ ์ฒ ํ•™์ž์™€ ๊ฐ™์€ ์ˆ˜์˜ ํฌํฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ ๊ทธ๋“ค ์‚ฌ์ด์— ํฌํฌ๋ฅผ ๋ผ๊ณ  ์›ํƒ์— ์•‰์Šต๋‹ˆ๋‹ค. ์ฒ ํ•™์ž๋Š” ๋์—†๋Š” ์Œ์‹์„ ์ ‘์‹œ์—์„œ ๋จน๊ณ  ์ƒ๊ฐํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒ ํ•™์ž๋ฅผ ๋จน์œผ๋ ค๋ฉด ํฌํฌ ๋‘ ๊ฐœ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค(๋งˆ์ง€๋ง‰ ํฌํฌ๋Š” ์ฒซ ๋ฒˆ์งธ ํฌํฌ์™€ ๊ณต์œ ). ํฌํฌ๋ฅผ ์ง‘๋Š” ๊ฒƒ๊ณผ ๋‚ด๋ ค๋†“๋Š” ๊ฒƒ์€ ๋ณ„๊ฐœ์˜ ํ–‰๋™์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ์ฒ ํ•™์ž๋Š” ์นจ๋ฌตํ•ฉ๋‹ˆ๋‹ค. ๊ณผ์ œ๋Š” XNUMX๋…„์ด ์ง€๋‚˜๋„ ๋ชจ๋‘๊ฐ€ ์ƒ๊ฐํ•˜๊ณ  ์ฑ„์›Œ์ง€๋Š” ๊ทธ๋Ÿฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ฐพ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋จผ์ € ๊ณต์œ  ๊ณต๊ฐ„์„ ํ™œ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํฌํฌ๋Š” ์ผ๋ฐ˜ ํ…Œ์ด๋ธ”์— ๋†“์—ฌ ์žˆ๊ณ  ์ฒ ํ•™์ž๋“ค์€ ํฌํฌ๊ฐ€ ์žˆ์„ ๋•Œ ๊ฐ€์ ธ๊ฐ”๋‹ค๊ฐ€ ๋‹ค์‹œ ๋†“์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ๋™๊ธฐํ™”์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ธ์ œ ํ™•์‹คํ•˜๊ฒŒ ๋ฒ ํŒ…ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? ํฌํฌ๊ฐ€ ์—†๋‹ค๋ฉด? ๊ธฐํƒ€ ๊ทธ๋Ÿฌ๋‚˜ ๋จผ์ € ์ฒ ํ•™์ž๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์Šค๋ ˆ๋“œ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ์„ ํ†ตํ•ด ์Šค๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. 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์€ ์ด๋Ÿฌํ•œ ์ž‘์—…์˜ ์ˆ˜์— ๋”ฐ๋ผ ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  AppDomain์„ ์œ„ํ•œ ํ•˜๋‚˜์˜ ํ’€. ์ด ํ’€์€ ๊ฑฐ์˜ ํ•ญ์ƒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ, ๋Œ€๊ธฐ์—ด ๋“ฑ์„ ์ƒ์„ฑ, ์‚ญ์ œํ•˜๋Š” ๋ฐ ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ํ’€ ์—†์ด๋„ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์ง์ ‘ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Thread, ์ด๊ฒƒ์€ ์Šค๋ ˆ๋“œ์˜ ์šฐ์„  ์ˆœ์œ„๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, ๊ธด ์ž‘์—…์ด ์žˆ๋Š” ๊ฒฝ์šฐ, Foreground ์Šค๋ ˆ๋“œ ๋“ฑ์˜ ๊ฒฝ์šฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, System.Threading.Tasks.Task ํด๋ž˜์Šค๋Š” ๋™์ผํ•ฉ๋‹ˆ๋‹ค Thread, ๊ทธ๋Ÿฌ๋‚˜ ๋ชจ๋“  ์ข…๋ฅ˜์˜ ํŽธ์˜: ๋‹ค๋ฅธ ์ž‘์—… ๋ธ”๋ก ํ›„์— ์ž‘์—…์„ ์‹คํ–‰ํ•˜๊ณ , ํ•จ์ˆ˜์—์„œ ๋ฐ˜ํ™˜ํ•˜๊ณ , ํŽธ๋ฆฌํ•˜๊ฒŒ ์ค‘๋‹จํ•˜๋Š” ๋“ฑ์˜ ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋“ฑ async / await ๊ตฌ์„ฑ์„ ์ง€์›ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค(ํƒœ์Šคํฌ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ํŒจํ„ด, 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์ž…๋‹ˆ๋‹ค.

ํ’๋ถ€ํ•œ ์ฒ ํ•™์ž ๋˜๋Š” ๊ฒฝ์Ÿ๋ ฅ ์žˆ๋Š” .NET ํ”„๋กœ๊ทธ๋ž˜๋ฐ

๊ทธ๋ฆผ์—์„œ ์ฐจ๋‹จ ์Šค๋ ˆ๋“œ(๊ต์ฐฉ ์ƒํƒœ). ๋…น์ƒ‰ - ์‹คํ–‰, ๋นจ๊ฐ„์ƒ‰ - ๋™๊ธฐํ™”, ํšŒ์ƒ‰ - ์Šค๋ ˆ๋“œ๊ฐ€ ์ž ์ž๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ๋ฆ„๋ชจ๊ผด์€ ์ž‘์—…์˜ ์‹œ์ž‘ ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

์ฒ ํ•™์ž์˜ ๊ตถ์ฃผ๋ฆผ

ํŠน๋ณ„ํžˆ ๋งŽ์€ ์Œ์‹์„ ์ƒ๊ฐํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ ๋ฐฐ๊ณ ํ””์€ ๋ˆ„๊ตฌ๋‚˜ ์ฒ ํ•™์„ ํฌ๊ธฐํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ฌธ์ œ์—์„œ ์Šค๋ ˆ๋“œ ๊ธฐ์•„ ์ƒํ™ฉ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•ด ๋ด…์‹œ๋‹ค. ๊ธฐ์•„๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ์‹คํ–‰ ์ค‘์ด์ง€๋งŒ ์ค‘์š”ํ•œ ์ž‘์—…์ด ์—†๋Š” ๊ฒฝ์šฐ, ์ฆ‰ ๋™์ผํ•œ ๊ต์ฐฉ ์ƒํƒœ์ด๋ฉฐ ์ด์ œ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž ์ž์ง€ ์•Š๊ณ  ์ ๊ทน์ ์œผ๋กœ ๋จน์„ ๊ฒƒ์„ ์ฐพ๊ณ  ์žˆ์ง€๋งŒ ์Œ์‹์ด ์—†๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๋นˆ๋ฒˆํ•œ ์ฐจ๋‹จ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค๋ฅธ ํฌํฌ๋ฅผ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์—†์œผ๋ฉด ํฌํฌ๋ฅผ ๋‹ค์‹œ ๋„ฃ์Šต๋‹ˆ๋‹ค.

// ะขะพ ะถะต ั‡ั‚ะพ ะธ ะฒ 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๋ช… ์ค‘ XNUMX๋ช…์€ ์™ผ์ชฝ ํฌํฌ๋ฅผ ๋‚ด๋ ค๋†“๋Š” ๊ฒƒ์„ ์žŠ์—ˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋“ค์€ ๋” ๋งŽ์€ ์Œ์‹์„ ๋จน๊ณ  ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์€ ๊ตถ์ฃผ๋ฆฌ๊ธฐ ์‹œ์ž‘ํ•˜์ง€๋งŒ ์Šค๋ ˆ๋“œ์˜ ์šฐ์„  ์ˆœ์œ„๋Š” ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๊ทธ๋“ค์€ ์™„์ „ํžˆ ๊ตถ์ฃผ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚˜์œ ์ฒ ํ•™์ž๋Š” ๋•Œ๋•Œ๋กœ ํฌํฌ๋ฅผ ๋‹ค์‹œ ๋„ฃ์Šต๋‹ˆ๋‹ค. ์ข‹์€ ์‚ฌ๋žŒ์€ ๋‚˜์œ ์‚ฌ๋žŒ๋ณด๋‹ค ์•ฝ XNUMX๋ฐฐ ์ ๊ฒŒ ๋จน๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ฝ”๋“œ์˜ ์ž‘์€ ์˜ค๋ฅ˜๋Š” ์„ฑ๋Šฅ ์ €ํ•˜๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค. ๋ชจ๋“  ์ฒ ํ•™์ž๊ฐ€ ์™ผ์ชฝ ํฌํฌ๋ฅผ ์ทจํ•˜๊ณ  ์˜ค๋ฅธ์ชฝ ํฌํฌ๊ฐ€ ์—†์œผ๋ฉฐ ์™ผ์ชฝ์„ ๋†“๊ณ  ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋‹ค์‹œ ์™ผ์ชฝ์„ ์ทจํ•˜๋Š” ๋“ฑ์˜ ๋“œ๋ฌธ ์ƒํ™ฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ ๋„ ์—ฌ๊ธฐ์„œ ์ฃผ๋ชฉํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ƒํ™ฉ์€ ๋˜ํ•œ ๊ต์ฐฉ ์ƒํƒœ์— ๊ฐ€๊นŒ์šด ๊ธฐ์•„ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๋ฐ˜๋ณตํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ๋‘ ๋ช…์˜ ๋‚˜์œ ์ฒ ํ•™์ž๊ฐ€ ํฌํฌ๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ๊ฐ”๊ณ  ๋‘ ๋ช…์˜ ์ข‹์€ ์ฒ ํ•™์ž๊ฐ€ ๊ตถ์ฃผ๋ฆฐ ์ƒํ™ฉ์— ๋Œ€ํ•œ ์‚ฌ์ง„์ž…๋‹ˆ๋‹ค.

ํ’๋ถ€ํ•œ ์ฒ ํ•™์ž ๋˜๋Š” ๊ฒฝ์Ÿ๋ ฅ ์žˆ๋Š” .NET ํ”„๋กœ๊ทธ๋ž˜๋ฐ

์—ฌ๊ธฐ์—์„œ ์Šค๋ ˆ๋“œ๊ฐ€ ๋•Œ๋•Œ๋กœ ๊นจ์–ด๋‚˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. XNUMX๊ฐœ์˜ ์ฝ”์–ด ์ค‘ XNUMX๊ฐœ๋Š” ์•„๋ฌด ์ž‘์—…๋„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์œ„์˜ ๋…น์ƒ‰ ๊ทธ๋ž˜ํ”„).

์ฒ ํ•™์ž์˜ ์ฃฝ์Œ

๊ธ€์Ž„์š”, ์ฒ ํ•™์ž๋“ค์˜ ์˜๊ด‘์Šค๋Ÿฌ์šด ์ €๋… ์‹์‚ฌ๋ฅผ ๋ฐฉํ•ด ํ•  ์ˆ˜์žˆ๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋Š” ๊ทธ๋“ค ์ค‘ ํ•œ ๋ช…์ด ์†์— ํฌํฌ๋ฅผ ๋“ค๊ณ  ๊ฐ‘์ž๊ธฐ ์ฃฝ๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค (๊ทธ๋ฆฌ๊ณ  ๊ทธ๋“ค์€ ๊ทธ๋ฅผ ๊ทธ๋ ‡๊ฒŒ ๋ฌป์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค). ๊ทธ๋Ÿฌ๋ฉด ์ด์›ƒ๋“ค์€ ์ €๋…์„ ๋จน์ง€ ์•Š๊ณ  ๋‚จ๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ƒ๊ฐํ•ด ๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฒ„๋ ค์ง‘๋‹ˆ๋‹ค. 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 (๊ฑฐ๊ธฐ์„œ ์‚ฌ์šฉ๋จ). ์ด์ œ ๊ทธ๋Š” ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‚ฌ๋žŒ๋“ค์˜ ์ˆ˜๋ฅผ ์„ธ๋Š” ๋ฐฉ๋ฒ•, ๊ทธ๋“ค์„ ์กฐ๊ธˆ ์žฌ์šฐ๋Š” ๋ฐฉ๋ฒ• ๋“ฑ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐํƒ€ ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ฒƒ์„ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹ํ”„๋กœ์„ธ์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์žก์•„๋จน๊ณ  ํ๋ฆ„์„ ์œ ์ง€ํ•˜๋Š” ๋™์ผํ•œ ํ™œ์„ฑ ์ฃผ๊ธฐ๋ผ๋Š” ์ ์„ ๊ธฐ์–ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฒ ํ•™์ž ์ค‘ ํ•œ ์‚ฌ๋žŒ์ด ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์ง€๋งŒ ๊ณจ๋“  ํฌํฌ(์šฐ์„ ์ˆœ์œ„ ์—ญ์ „ ๋ฌธ์ œ)๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๊ธฐ์•„๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. . ๋”ฐ๋ผ์„œ ์ œXNUMX์ž ํ˜ธ์ถœ, ์ค‘์ฒฉ ์ž ๊ธˆ ๋ฐ ๊ธฐํƒ€ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์ƒํ™ฉ ์—†์ด ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ์˜ ๋งค์šฐ ์งง์€ ๋ณ€๊ฒฝ์—๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

ํ’๋ถ€ํ•œ ์ฒ ํ•™์ž ๋˜๋Š” ๊ฒฝ์Ÿ๋ ฅ ์žˆ๋Š” .NET ํ”„๋กœ๊ทธ๋ž˜๋ฐ

๊ทธ๋ฆฌ๊ธฐ SpinLock. ์‹œ๋ƒ‡๋ฌผ์€ ํ™ฉ๊ธˆ ํฌํฌ๋ฅผ ์œ„ํ•ด ๋Š์ž„์—†์ด "์‹ธ์›€"ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‹คํŒจ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค - ๊ทธ๋ฆผ์—์„œ ์„ ํƒํ•œ ์˜์—ญ์ž…๋‹ˆ๋‹ค. ์ฝ”์–ด๋Š” ์™„์ „ํžˆ ํ™œ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด 2๊ฐœ์˜ ์Šค๋ ˆ๋“œ์— ์˜ํ•ด ์•ฝ 3/XNUMX๋งŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋˜ ๋‹ค๋ฅธ ํ•ด๊ฒฐ์ฑ…์€ Interlocked.CompareExchange ์œ„์˜ ์ฝ”๋“œ(๊ตถ์ฃผ๋ฆฐ ์ฒ ํ•™์ž์—์„œ)์— ํ‘œ์‹œ๋œ ๊ฒƒ๊ณผ ๋™์ผํ•œ ํ™œ์„ฑ ๋Œ€๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์ด๋ฏธ ๋งํ–ˆ๋“ฏ์ด ์ด๋ก ์ ์œผ๋กœ ์ฐจ๋‹จ์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ฝ Interlocked ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ CompareExchange, ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์›์ž ์ฝ๊ธฐ ๋ฐ ์“ฐ๊ธฐ๋ฅผ ์œ„ํ•œ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ณ€๊ฒฝ์˜ ๋ฐ˜๋ณต์„ ํ†ตํ•ด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ณ€๊ฒฝ์„ ํ•  ์‹œ๊ฐ„์ด ์žˆ๋Š” ๊ฒฝ์šฐ(read 1, read 2, write 2, write 1 ๋ถˆ๋Ÿ‰) ๋‹จ์ผ ๊ฐ’์— ๋Œ€ํ•œ ๋ณต์žกํ•œ ๋ณ€๊ฒฝ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(Interlocked Anything ํŒจํ„ด). .

์ปค๋„ ๋ชจ๋“œ ์†”๋ฃจ์…˜

๋ฃจํ”„์—์„œ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์šฐ๋ฆฌ์˜ ์˜ˆ๋ฅผ ๊ณ„์†ํ•ด์„œ ์›จ์ดํ„ฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ฒ ํ•™์ž๋ฅผ ์ž ๋“ค๊ฒŒ ํ•˜๊ณ  ํ•„์š”ํ•  ๋•Œ๋งŒ ๊นจ์šฐ๋Š”์ง€ ๋ด…์‹œ๋‹ค. ๋จผ์ € ์šด์˜ ์ฒด์ œ์˜ ์ปค๋„ ๋ชจ๋“œ๋ฅผ ํ†ตํ•ด ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ตฌ์กฐ๋Š” ์ข…์ข… ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์˜ ๊ตฌ์กฐ๋ณด๋‹ค ๋Š๋ฆฝ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ช‡ ๋ฐฐ ๋” ๋Š๋ฆฝ๋‹ˆ๋‹ค. AutoResetEvent ์•„๋งˆ๋„ 53๋ฐฐ ๋” ๋Š๋ฆด ๊ฒƒ์ž…๋‹ˆ๋‹ค. SpinLock [๋ฆฌํžˆํ„ฐ]. ๊ทธ๋Ÿฌ๋‚˜ ๋„์›€์„ ๋ฐ›์œผ๋ฉด ๊ด€๋ฆฌ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ์‹œ์Šคํ…œ ์ „์ฒด์—์„œ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋™๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๊ธฐ๋ณธ ๊ตฌ์„ฑ์€ ๋ฐ˜์„ธ๊ธฐ ์ „์— Dijkstra๊ฐ€ ์ œ์•ˆํ•œ ์„ธ๋งˆํฌ์–ด์ž…๋‹ˆ๋‹ค. ์„ธ๋งˆํฌ์–ด๋Š” ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ ์‹œ์Šคํ…œ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ์–‘์˜ ์ •์ˆ˜์ด๋ฉฐ ์ฆ๊ฐ€ ๋ฐ ๊ฐ์†Œ๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ์ž‘์—…์ด ์žˆ์Šต๋‹ˆ๋‹ค. XNUMX์œผ๋กœ ๊ฐ์†Œํ•˜์ง€ ๋ชปํ•˜๋ฉด ํ˜ธ์ถœ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ™œ์„ฑ ์Šค๋ ˆ๋“œ/ํ”„๋กœ์„ธ์Šค์— ์˜ํ•ด ์ˆซ์ž๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด ์Šค๋ ˆ๋“œ๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  ์„ธ๋งˆํฌ์–ด๋Š” ์ „๋‹ฌ๋œ ์ˆซ์ž๋งŒํผ ๋‹ค์‹œ ๊ฐ์†Œํ•ฉ๋‹ˆ๋‹ค. ์„ธ๋งˆํฌ์–ด๊ฐ€ ์žˆ๋Š” ๋ณ‘๋ชฉ ํ˜„์ƒ์— ์žˆ๋Š” ๊ธฐ์ฐจ๋ฅผ ์ƒ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. .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) ๊ทธ๋Ÿฌ๋ฉด ์ฒ ํ•™์ž๊ฐ€ ๊ฐœ๋ฐœ์ž๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฝ”๋“œ์—์„œ ์˜ค๋ฅ˜๋ฅผ ์ฐพ๋Š” ๊ฒƒ์€ ์ƒ๋‹นํžˆ ์–ด๋ ค์šด ์ž‘์—…์ž…๋‹ˆ๋‹ค. ์ด ์†”๋ฃจ์…˜์˜ ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋Š” ์ผ๋ถ€ ์ฒ ํ•™์ž๊ฐ€ ๋ฐฐ๊ณ ํ”„์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๊ณ  ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์†”๋ฃจ์…˜

ํƒ€์ด๋ฐ์— ๋Œ€ํ•œ ๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹, ์ฆ‰ ์‚ฌ์šฉ์ž ๋ชจ๋“œ์™€ ๋ฃจํ”„์— ๋จธ๋ฌด๋ฅผ ๋•Œ์™€ ์ปค๋„์„ ํ†ตํ•ด ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•  ๋•Œ๋ฅผ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ์งง์€ ์ž ๊ธˆ์— ์ ํ•ฉํ•˜๊ณ  ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ๊ธด ์ž ๊ธˆ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € ๋ฃจํ”„์—์„œ ๋ณ€์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๊นŒ์ง€ ์ž ์‹œ ๊ธฐ๋‹ค๋ฆฐ ๋‹ค์Œ ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง€๋ฉด ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ์†Œ์œ„ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค. ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ตฌ์กฐ. ๋‹ค์Œ์€ ์ปค๋„ ๋ชจ๋“œ์™€ ๋™์ผํ•œ ๊ตฌ์„ฑ์ด์ง€๋งŒ ์ด์ œ ์‚ฌ์šฉ์ž ๋ชจ๋“œ ๋ฃจํ”„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. SemaphorSlim, ManualResetEventSlim ๋“ฑ ์—ฌ๊ธฐ์—์„œ ๊ฐ€์žฅ ์ธ๊ธฐ์žˆ๋Š” ๋””์ž์ธ์€ Monitor, ์™œ๋ƒํ•˜๋ฉด C#์—๋Š” ์ž˜ ์•Œ๋ ค์ง„ lock ํ†ต์‚ฌ๋ก . Monitor ์ด๊ฒƒ์€ ์ตœ๋Œ€๊ฐ’์ด 1(๋ฎคํ…์Šค)์ธ ๋™์ผํ•œ ์„ธ๋งˆํฌ์–ด์ด์ง€๋งŒ ๋ฃจํ”„์—์„œ ๋Œ€๊ธฐ, ์žฌ๊ท€, ์กฐ๊ฑด ๋ณ€์ˆ˜ ํŒจํ„ด(์•„๋ž˜์—์„œ ์ž์„ธํžˆ ์„ค๋ช…) ๋“ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์†”๋ฃจ์…˜์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// ะกะฟั€ัั‡ะตะผ ะพะฑัŠะตะบั‚ ะดะปั ะœะพะฝะธั‚ะพั€ะฐ ะพั‚ ะฒัะตั…, ั‡ั‚ะพะฑั‹ ะฑะตะท ะดะตะดะปะพะบะพะฒ.
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);
    }
}

์—ฌ๊ธฐ์—์„œ ์šฐ๋ฆฌ๋Š” ํฌํฌ์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•ด ์ „์ฒด ํ…Œ์ด๋ธ”์„ ๋‹ค์‹œ ์ฐจ๋‹จํ•˜์ง€๋งŒ ์ด์ œ๋Š” ๋ˆ„๊ตฐ๊ฐ€ ์‹์‚ฌ๋ฅผ ๋งˆ์ณค์„ ๋•Œ ์ด์›ƒ์ด ์•„๋‹Œ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๋ฅผ ํ•œ ๋ฒˆ์— ์ฐจ๋‹จ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค. ์ €๊ฒƒ๋“ค. ๋จผ์ € ๋ˆ„๊ฐ€ ๋จน๊ณ  ์ด์›ƒ์„ ๋ง‰๊ณ , ์ด ์‚ฌ๋žŒ์ด ๋‹ค ๋จน์—ˆ๋Š”๋ฐ ๋ฐ”๋กœ ๋˜ ๋จน๊ณ  ์‹ถ์œผ๋ฉด ๋ง‰๊ณ  ์ด์›ƒ์„ ๊นจ์šฐ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ์ ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๊ต์ฐฉ ์ƒํƒœ์™€ ์ผ๋ถ€ ์ฒ ํ•™์ž์˜ ๊ธฐ์•„๋ฅผ ํ”ผํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์งง์€ ๋Œ€๊ธฐ์—๋Š” ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๊ธด ๋Œ€๊ธฐ์—๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์‚ฌ๋žŒ์„ ํ•œ ๋ฒˆ์— ์ฐจ๋‹จ ํ•ด์ œํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์Œ ์†”๋ฃจ์…˜์—์„œ์™€ ๊ฐ™์ด ์ด์›ƒ๋งŒ ์ฐจ๋‹จ ํ•ด์ œํ•œ ๊ฒฝ์šฐ๋ณด๋‹ค ๋Š๋ฆฝ๋‹ˆ๋‹ค. AutoResetEvent๊ทธ๋Ÿฌ๋‚˜ ๊ทธ ์ฐจ์ด๋Š” ํฌ์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ๋Š” ๋จผ์ € ์‚ฌ์šฉ์ž ๋ชจ๋“œ์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ะฃ lock ๊ตฌ๋ฌธ์—๋Š” ๋ถˆ์พŒํ•œ ๋†€๋ผ์›€์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค Monitor ์ง์ ‘ [Richter] [Eric Lippert]. ๊ทธ ์ค‘ ํ•˜๋‚˜๋Š” lock ํ•ญ์ƒ ๋ฐ–์œผ๋กœ Monitor, ์˜ˆ์™ธ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ๊ต์ฐฉ ์ƒํƒœ์— ๋น ์ง€๊ฑฐ๋‚˜ ํ”„๋กœ๊ทธ๋žจ์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ข…๋ฃŒํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ๋†€๋ผ์šด ์ ์€ Monitor๊ฐ€ ๋™๊ธฐํ™” ๋ธ”๋ก(SyncBlock), ๋ชจ๋“  ๊ฐœ์ฒด์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ถ€์ ์ ˆํ•œ ๊ฐœ์ฒด๋ฅผ ์„ ํƒํ•˜๋ฉด ์‰ฝ๊ฒŒ ๊ต์ฐฉ ์ƒํƒœ์— ๋น ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: ์ธํ„ด๋œ ๋ฌธ์ž์—ด์— ์ž ๊ทธ๋Š” ๊ฒฝ์šฐ). ์ด๋ฅผ ์œ„ํ•ด ํ•ญ์ƒ ์ˆจ๊ฒจ์ง„ ๊ฐœ์ฒด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์กฐ๊ฑด ๋ณ€์ˆ˜ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ์กฐ๊ฑด์— ๋Œ€ํ•œ ์˜ˆ์ƒ์„ ๋ณด๋‹ค ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. .NET์—์„œ๋Š” ๋ถˆ์™„์ „ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋ก ์ ์œผ๋กœ ํ•˜๋‚˜์˜ ๋กœํฌ๊ฐ€ ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ ๋ณ€์ˆ˜(Posix ์Šค๋ ˆ๋“œ์—์„œ์™€ ๊ฐ™์ด)์— ์—ฌ๋Ÿฌ ํ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ชจ๋“  ์ฒ ํ•™์ž๋ฅผ ์œ„ํ•ด ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ํ˜•ํƒœ์—์„œ๋„ ์ฝ”๋“œ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŽ์€ ์ฒ ํ•™์ž ๋˜๋Š” async / await

์ด์ œ ํšจ๊ณผ์ ์œผ๋กœ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋งŒ์•ฝ ์šฐ๋ฆฌ์—๊ฒŒ ๋งŽ์€ ์ฒ ํ•™์ž๋“ค์ด ์žˆ๋‹ค๋ฉด ์–ด๋–จ๊นŒ์š”? 100? 10000? ์˜ˆ๋ฅผ ๋“ค์–ด ์›น ์„œ๋ฒ„์— ๋Œ€ํ•œ 100000๊ฐœ์˜ ์š”์ฒญ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•ด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์€ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋„ˆ๋ฌด ๋งŽ์€ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋…ผ๋ฆฌ ์ฝ”์–ด๊ฐ€ ์žˆ๋Š” ๋งŒํผ๋งŒ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค(์ €๋Š” 4๊ฐœ ์žˆ์Šต๋‹ˆ๋‹ค). ๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ ๋ชจ๋“  ์‚ฌ๋žŒ๋“ค์€ ์ž์›์„ ๋นผ์•—์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ํ•œ ๊ฐ€์ง€ ํ•ด๊ฒฐ์ฑ…์€ async/await ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ๊ทธ ์•„์ด๋””์–ด๋Š” ๋ฌด์–ธ๊ฐ€๊ฐ€ ๊ณ„์†๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ํ•จ์ˆ˜๊ฐ€ ์Šค๋ ˆ๋“œ๋ฅผ ๋ณด์œ ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ค ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์‹คํ–‰์„ ๋‹ค์‹œ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค(๋ฐ˜๋“œ์‹œ ๋™์ผํ•œ ์Šค๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰ํ•  ํ•„์š”๋Š” ์—†์Œ!). ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ ํฌํฌ๋ฅผ ๊ธฐ๋‹ค๋ฆด ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋Š” ๊ฐ๊ฐ ์•ฝ 2ms ๋™์•ˆ ์œ ํœด ์ƒํƒœ์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  async/await ์†”๋ฃจ์…˜์€ ํ‰๊ท  ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ๊ฐ๊ฐ 100์ดˆ์ธ 6.8๊ฐœ๋ฅผ ๋ชจ๋‘ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์‹ค์ œ ์‹œ์Šคํ…œ์—์„œ๋Š” 6์ดˆ ๋™์•ˆ ์œ ํœด ์ƒํƒœ๊ฐ€ ๋˜๋Š” ๊ฒƒ์€ ํ—ˆ์šฉ๋˜์ง€ ์•Š์œผ๋ฉฐ ์ด๋ ‡๊ฒŒ ๋งŽ์€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. Monitor๋ฅผ ์‚ฌ์šฉํ•œ ์†”๋ฃจ์…˜์€ ์ „ํ˜€ ํ™•์žฅํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์œผ๋กœ ํŒ๋ช…๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

์ด๋Ÿฌํ•œ ์ž‘์€ ์˜ˆ์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด .NET์€ ๋งŽ์€ ๋™๊ธฐํ™” ๊ตฌ์กฐ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์ด ํ•ญ์ƒ ๋ช…ํ™•ํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด ๊ธฐ์‚ฌ๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์ด๊ฒƒ์ด ๋์ด์ง€๋งŒ ์—ฌ์ „ํžˆ ์Šค๋ ˆ๋“œ ์•ˆ์ „ ์ปฌ๋ ‰์…˜, TPL ๋ฐ์ดํ„ฐ ํ๋ฆ„, ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ, ์†Œํ”„ํŠธ์›จ์–ด ํŠธ๋žœ์žญ์…˜ ๋ชจ๋ธ ๋“ฑ๊ณผ ๊ฐ™์€ ํฅ๋ฏธ๋กœ์šด ๊ฒƒ๋“ค์ด ๋งŽ์ด ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค.

์†Œ์Šค

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€