C# :: For Beginners

[C#] Thread 다뤄보기 – 3. Thread의 동기화

스레드 프로그래밍을 하다보면 전혀 예상치 못한 상황에 많이 부딪히게 됩니다.
그 상황의 대부분이 바로 동기화된 설계가 이루어 지지 않아서 입니다.

A ~ C 까지의 스레드가 하나의 고유 객체를 동시에 읽어 버리거나 값을 처리 할 경우 문제가 발생합니다.
아래 예제를 통해 살펴 봅시다.

using System.Collections.Generic;
using System.Text;
using System.Threading; // 스레드 네임스페이스 추가

namespace ThreadSync
{
    class Program
    {
        static void Main(string[] args)
        {
            // 동기화 객체를 생성합니다.
            SyncTest syncClass=new SyncTest();

            // 스레드 배열
            Thread[] syncThread=new Thread[5];
            for (int i = 0; i < 5; i++)
            {
                // 스레드를 시작합니다.
                syncThread[i] = new Thread(new ThreadStart(syncClass.CountPlus));
                syncThread[i].Start();
            }

            // 스레드가 모두 멈출 때 까지 기다립니다.
            Thread.Sleep(100);

            // 결과를 출력합니다.
            Console.WriteLine("Count : {0}",syncClass.Count);
        }
    }

    class SyncTest
    {
        // 전체 카운트
        public int Count=0;

        public void CountPlus()
        {
            // Count 의 값을 할당해줍니다.
            int temp = Count;

            // 현재 스레드 ID 와 temp 값을 출력합니다.
            Console.WriteLine("Thread{0} : {1} ", 
            Thread.CurrentThread.ManagedThreadId, temp);

            // Count 에 1 을 증가
            temp = temp + 1;
            Thread.Sleep(1);
            Count = temp;
        }
    }
}

 

실행 결과

Thread3 : 0
Thread4 : 1
Thread5 : 1
Thread6 : 1
Thread7 : 1

Count : 2

 

위와 같은 결과가 나오긴 했지만, 실행 할 때 마다 다른 결과가 나오게 될 것입니다.

그렇다면 왜 이런 결과가 나오는지 알아보겠습니다.

먼저 소스를 살펴 봅시다. 15 번째 줄에 배열 형태로 스레드를 선언하였습니다. 그리고 아래 for 문을 이용하여 스레드 객체를 생성하여 실행시킵니다.
예상되는 결과는 당연 5가 나와야 됩니다. 스레드를 5 번 실행하므로 당연한 결과를 예상할 수 있습니다.

하지만 결과는 0 이나 1의 전혀 예상치 못한 결과가 나옵니다.
그 이유는 앞서 설명한 대로 스레드가 동시에 하나의 전역 변수를 사용하기 때문입니다.
A 라는 스레드에서 Count 값에 대한 설정을 마치기도 전에 B라는 스레드에서도 똑같이 Count 값을 가져간 것입니다.

스레드 B 는 스레드 A 가 Count 값을 처리 하기도 전에 Count 값을 가져갔기 때문에 문제가 되는 것입니다.
이 상황을 해결 하기 위해서는 어쩔 수 없이 스레드 B 는 스레드 A 가 Count 를 모두 소진할 때 까지 기다려야만 합니다.
그래서 등장한 개념이 동기화 이고, 이제 가장 많이 사용하는 동기화 예제를 적용시켜 보겠습니다.
위에 작성한 예제 중 CountPlus() 메서드를 아래와 같이 수정해봅시다.

 

public void CountPlus()
{
    lock (this)
    {
        // Count 의 값을 할당합니다.
        int temp = Count;

        // 현재 스레드 ID 와 temp 값을 출력합니다.
        Console.WriteLine("Thread{0} : {1} ", 
        Thread.CurrentThread.ManagedThreadId, temp);

        // Count 에 1 을 증가시킵니다.
        temp = temp + 1;
        Thread.Sleep(1);
        Count = temp;
    }
}

 

실행 결과

Thread3 : 0
Thread4 : 1
Thread5 : 2
Thread6 : 3
Thread7 : 4

Count : 5

 

“lock” 이라는 키워드를 이용해서 동기화 작업을 한 후에 다시 결과를 확인해 보면 이제 올바른 결과가 출력 된 것을 볼 수 있습니다.
여기서 사용한 동기화 예제에서는 “lock” 이라는 키워드를 사용하였습니다.
“lock” 키워드는 동기화 작업을 하고 싶은 영역을 위와 같이 잡아주면, 지정된 부분은 단 하나의 스레드에만 접근 할 수 있게 됩니다.

고맙습니다.

Leave a Reply

Discover more from Dream big, Achieve more.

Subscribe now to keep reading and get access to the full archive.

Continue reading