[C#] μŠ€λ ˆλ”©

[원본 링크]

C#μ—μ„œ μŠ€λ ˆλ”©μ˜ μ‚¬μš©λ²•μ€ κ°„λ‹¨ν•˜λ‹€.
Thread ν΄λž˜μŠ€μ— ν•¨μˆ˜λ₯Ό 인자둜 μ€˜μ„œ μƒμ„±ν•˜κ³ ,
Start()λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄ λœλ‹€.
μ—¬κΈ°μ—μ„œ ν•¨μˆ˜λŠ” 객체의 λ©”μ„œλ“œλ‚˜ 정적 λ©”μ„œλ“œ, λžŒλ‹€μ‹μ΄ λ‹€ λ“€μ–΄κ°ˆ 수 μžˆλ‹€.
λŒ€μ‹  ν•¨μˆ˜μ˜ ν˜•μ‹μ€ void(void)μ—¬μ•Όλ§Œ ν•œλ‹€. λ¦¬ν„΄μ΄λ‚˜ νŒŒλΌλ―Έν„°κ°€ 있으면 μ•ˆλœλ‹€.

μ•„λž˜λŠ” μ‹€ν–‰ μ˜ˆμ‹œλ‹€.

using System;
using System.Threading;
using System.Collections.Generic;

namespace ThreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("main thread μ‹œμž‘");

            var threads = new List<Thread>();
            threads.Add(new Thread(() => Console.WriteLine("첫번째 thread")));
            threads.Add(new Thread(() => Console.WriteLine("λ‘λ²ˆμ§Έ thread")));
            threads.Add(new Thread(() => Console.WriteLine("μ„Έλ²ˆμ§Έ thread")));
            threads.Add(new Thread(() => Console.WriteLine("λ„€λ²ˆμ§Έ thread")));

            //μ „λΆ€ 돌림
            foreach (var thread in threads)
                thread.Start();

            Console.WriteLine("main thread μ’…λ£Œ");
        }
    }
}

잘 λŒμ•„κ°„λ‹€.
μˆœμ„œκ°€ μ œλ©‹λŒ€λ‘œμΈκ±΄ μ–΄μ©”μˆ˜μ—†λŠ” μŠ€λ ˆλ”©μ˜ μˆ™λͺ…이닀.

μŠ€λ ˆλ“œκ°€ λ‹€ λλ‚ λ•ŒκΉŒμ§€ main을 λŒ€κΈ°μ‹œν‚€λ €λ©΄ join을 μ“°λ©΄ λœλ‹€.


그리고 데이터 λ ˆμ΄μŠ€λΌλŠ” 것이 μžˆλ‹€.
μ—¬λŸ¬ μŠ€λ ˆλ“œμ—μ„œ ν•˜λ‚˜μ˜ λ³€μˆ˜λ₯Ό κ³΅μœ ν•  λ•Œ λ°œμƒν•˜λŠ” 문제인데, 예제λ₯Ό 톡해 ν•œλ²ˆ 봐보자
ν•˜λ‚˜μ˜ λ³€μˆ˜λ₯Ό μ—¬λŸ¬ μŠ€λ ˆλ“œμ—μ„œ λ™μ‹œμ— μ¦κ°€μ‹œν‚€λŠ” μ½”λ“œλ‹€.

using System;
using System.Threading;
using System.Collections.Generic;

namespace ThreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("main thread μ‹œμž‘");

            var threads = new List<Thread>();
            int counter = 0;

            //10000μ”© μ¦κ°€μ‹œν‚΄
            ThreadStart inc = () => {
                for (int i = 0; i < 10000; i++)
                    counter++;
            };

            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));

            //μ „λΆ€ 돌림
            foreach (var thread in threads)
                thread.Start();

            //μ΄λ†ˆλ“€ λλ‚ λ•ŒκΉŒμ§€ main λΈ”λŸ­
            foreach (var thread in threads)
                thread.Join();

            Console.WriteLine($"{counter}");

            Console.WriteLine("main thread μ’…λ£Œ");
        }
    }
}

λΆ„λͺ… 40000이 λ‚˜μ™€μ•Όν•˜λŠ”λ° 그거보닀 λœλ‚˜μ™”λ‹€.
값을 μ˜¬λ¦¬λŠ” 와쀑에 μŠ€λ ˆλ“œλΌλ¦¬ μΆ©λŒμ„ ν•΄μ„œ λ―ΈμŠ€κ°€ λ‚œ 것인데, 이걸 ν•΄κ²°ν•˜λ €λ©΄ Mutex둜 μž„κ³„κ΅¬μ—­μ„ μ„€μ •ν•΄μ£Όκ±°λ‚˜ Atomic λ“±μœΌλ‘œ 동기화λ₯Ό ν•΄μ€˜μ•Ό ν•œλ‹€.

μ•„λž˜λŠ” λŒ€λž΅μ μΈ mutex의 μ‚¬μš©λ²•μ΄λ‹€.

using System;
using System.Threading;
using System.Collections.Generic;

namespace ThreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("main thread μ‹œμž‘");

            var threads = new List<Thread>();
            int counter = 0;
            var locker = new Mutex();

            //10000μ”© μ¦κ°€μ‹œν‚΄
            ThreadStart inc = () => {
                locker.WaitOne(); //νšλ“ ν›„ 잠금
                for (int i = 0; i < 10000; i++)
                    counter++;
                locker.ReleaseMutex(); //잠금 ν•΄μ œ
            };

            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));

            //μ „λΆ€ 돌림
            foreach (var thread in threads)
                thread.Start();

            //μ΄λ†ˆλ“€ λλ‚ λ•ŒκΉŒμ§€ main λΈ”λŸ­
            foreach (var thread in threads)
                thread.Join();

            Console.WriteLine($"{counter}");

            Console.WriteLine("main thread μ’…λ£Œ");
        }
    }
}

잘 λœλ‹€.

그리고 Monitorλž€ λ†ˆλ„ μžˆλ‹€.
ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ‚¬μš©ν•  λ•Œλ§Œ μ ν•©ν•˜λ‹€. ν•˜μ§€λ§Œ Mutex보닀 μˆ˜μ‹­λ°°λŠ” λΉ λ₯΄λ‹€κ³  ν•œλ‹€.

using System;
using System.Threading;
using System.Collections.Generic;

namespace ThreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("main thread μ‹œμž‘");

            var threads = new List<Thread>();
            int counter = 0;
            var locker = new Object();

            //10000μ”© μ¦κ°€μ‹œν‚΄
            ThreadStart inc = () => {
                Monitor.Enter(locker); //잠금
                for (int i = 0; i < 10000; i++)
                    counter++;
                Monitor.Exit(locker); //잠금 ν•΄μ œ

             };

            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));

            //μ „λΆ€ 돌림
            foreach (var thread in threads)
                thread.Start();

            //μ΄λ†ˆλ“€ λλ‚ λ•ŒκΉŒμ§€ main λΈ”λŸ­
            foreach (var thread in threads)
                thread.Join();

            Console.WriteLine($"μΉ΄μš΄ν„°λŠ” {counter}");

            Console.WriteLine("main thread μ’…λ£Œ");
        }
    }
}

잘 λœλ‹€.

그리고 C#μ—μ„œλŠ” 이걸 λ¬Έλ²•μœΌλ‘œλ„ μ œκ³΅ν•œλ‹€.
λ”°λ‘œ κ³΅μœ ν•΄μ„œ 잠금 μΈμ‹μš©μœΌλ‘œ μ“Έ 객체λ₯Ό λ§Œλ“€κ³  lock λΈ”λŸ­μœΌλ‘œ μž κΈˆμ„ κ±°λŠ”κ²ƒμ΄λ‹€.

using System;
using System.Threading;
using System.Collections.Generic;

namespace ThreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("main thread μ‹œμž‘");

            var threads = new List<Thread>();
            int counter = 0;
            var locker = new Object(); //잠금용 객체

            //10000μ”© μ¦κ°€μ‹œν‚΄
            ThreadStart inc = () => {
                lock (locker) //λ“€μ–΄κ°€λ©΄ μž κ°€λ²„λ¦Ό
                {
                    for (int i = 0; i < 10000; i++)
                        counter++;
                }
             };

            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));

            //μ „λΆ€ 돌림
            foreach (var thread in threads)
                thread.Start();

            //μ΄λ†ˆλ“€ λλ‚ λ•ŒκΉŒμ§€ main λΈ”λŸ­
            foreach (var thread in threads)
                thread.Join();

            Console.WriteLine($"μΉ΄μš΄ν„°λŠ” {counter}");

            Console.WriteLine("main thread μ’…λ£Œ");
        }
    }
}

그런데 이런 μž„κ³„κ΅¬μ—­μ˜ 섀정은 μ„±λŠ₯을 μ—„μ²­λ‚˜κ²Œ μž‘μ•„λ¨ΉκΈ° λ•Œλ¬Έμ—, μ΅œμ†Œν•œμœΌλ‘œ μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€.


그리고 Atomic, κ·ΈλŸ¬λ‹ˆκΉŒ μ›μžμ  연산을 ν™œμš©ν•  μˆ˜λ„ μžˆλ‹€.

using System;
using System.Threading;
using System.Collections.Generic;

namespace ThreadingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("main thread μ‹œμž‘");

            var threads = new List<Thread>();
            int counter = 0;

            //10000μ”© μ¦κ°€μ‹œν‚΄
            ThreadStart inc = () => {
                for (int i = 0; i < 10000; i++)
                    Interlocked.Increment(ref counter); //atomic μ—°μ‚°
             };

            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));
            threads.Add(new Thread(inc));

            //μ „λΆ€ 돌림
            foreach (var thread in threads)
                thread.Start();

            //μ΄λ†ˆλ“€ λλ‚ λ•ŒκΉŒμ§€ main λΈ”λŸ­
            foreach (var thread in threads)
                thread.Join();

            Console.WriteLine($"μΉ΄μš΄ν„°λŠ” {counter}");

            Console.WriteLine("main thread μ’…λ£Œ");
        }
    }
}

이것도 잘 λœλ‹€.
λŒ€μ²΄λ‘œ Mutexλ₯Ό ν†΅ν•œ λΈ”λŸ­λ³΄λ‹€λŠ” μ›μžμ  연산을 ν™œμš©ν•˜λŠ” 것이 μ„±λŠ₯은 더 μ’‹λ‹€κ³ λ“€ ν•œλ‹€.