본문 바로가기

.NET/C#

Thread 란?

728x90
반응형
참 오랜만에 쓰는 포스팅이네요. 한동안 이것 저것 신경쓸게 많아서 포스팅을 잘 할 수 없었는데요. 앞으로는 어떻게든 짬을 내서 포스팅을 할까 합니다 ^^

아직 많이 부족한 실력이지만 저도 공부할겸 같이 포스팅도 하는 것이니 내용이 허접하더라도 많은 이해를 해주시면 좋겠네요 ^^

우선 Thread(스레드라 칭하겠습니다)에 대해서 간단히 알아보도록 하겠습니다.

컴퓨터에서 프로그램을 실행하게 되면 cpu에서 프로세스를 생성합니다. 프로세스를 동시에 여러개 처리할 수 있는 기능 멀티프로세스이고요. 여러개의 프로세스를 처리하면서 그 시간을 더 효율적으로 처리하는 방법을 연구하게 되었는데 그게 스레드의 개념입니다.
스래드는 프로세서가 프로세스 작업을 처리하기 위해 할당한 프로세스 수행 시간을 쪼개 한 주기에서 여러 개의 작업을 수행할 수 있도록 만든 기법을 말하는 것이죠. 즉 프로세스에 할당된 자원 내에서 특정 명령을 수행하는 모듈이라고 볼 수 있죠.
더 즉흥적이게 표현 한다면 프로세스 안의 작은 프로세스라고 할 수 도 있습니다.

우선 C#에서 스레드를 생성하는 방법을 알아보도록 하겠습니다.
 - Thread 클래스를 이용한 생성
 - ThreadPool 클래스를 이용한 생성
 - Timer 클래스를 이용한 생성

우선 위 3가지로 스레드를 생성할 수가 있습니다.

그럼 간단한 예제 프로그램을 가지고 스레드를 생성하지 않았을 때와 위 3가지 방법으로 스레드를 만드는 것을 보여드리도록 하겠습니다.

우선 Wihdows Forms 응용프로그램 으로 프로젝트를 하나 생성합니다. 프로젝트는 각자 원하는 이름으로 해주시면 되고요. 저는 ThreadTest란 프로젝트를 생성했습니다. 폼 모양은 아래 그림과 같습니다.

폼에 버튼이 5개가 있는 간단한 윈도우 폼입니다. 이제 버튼에 써져 있는대로 동기호출, 비동기호출를 구현해 보도록 하겠습니다. 우선 해당 프로그램의 속성에서 출력형식을 Windows Forms에서 콘솔로 변경을 해주시기 바랍니다. 그래야 정확한 테스트를 할 수가 있습니다. ^^

우선 동기호출을 해보도록 하겠습니다. 동기호출 버튼을 클릭시 스레드를 생성하지 않고 10초후에 메시지 박스를 띄우는 소스입니다.

private void btnSyncCall_Click(object sender, EventArgs e)

{

    TheWorker worker = new TheWorker();

    worker.DoWork();

    MessageBox.Show("작업을 종료했습니다.");

}


class TheWorker

{

    public void DoWork()

    {

        Console.WriteLine("Beginning of Dowork Method");

        for (int i = 0; i < 10; i++)

        {

            Thread.Sleep(1000);

        }

        Console.WriteLine("End of Dowork Method");

    }

 

    public void DoWork2(object state)

    {

        Console.WriteLine("Beginning of Dowork2 Method");

        for (int i = 0; i < 10; i++)

        {

            Thread.Sleep(1000);

        }

        Console.WriteLine("End of Dowork2 Method");

    }

}


위와 같이 되어 있을 때 동기호출를 클릭하면 10초동안 다른 작업을 할 수가 없을것입니다. 10초동안 위 프로그램은 다른 작업을 처리하지 않고 있을 것입니다. 10초 후에야 메시지 박스가 뜨는걸 확인할 수 있을 것입니다. 그 이유는 모든 리소스를 DoWork 메소드에서 잡고 있기 때문입니다.

그럼 이번에는 Thread 클래스를 이용해서 스레드를 생성해서 비동기 처리를 해보도록 하겠습니다.

private void btnAsyncCall1_Click(object sender, EventArgs e)

{

    TheWorker worker = new TheWorker();

    ThreadStart start = new ThreadStart(worker.DoWork);

    Thread thread = new Thread(start);

    thread.IsBackground = true;

    thread.Start();

    Thread.Sleep(1000);

    MessageBox.Show(thread.ThreadState.ToString());

}

다음과 같은 소스일 때 비동기처리(Thread)를 클릭하게 되면 콘솔창에 Dowork 메소드 시작을 알리고 1초후에 메시지박스가 뜨는걸 확인 할 수 있습니다. 보는 바와 같이 각 메소드 처리가 따로 따로 움직이는걸 볼 수 있죠. 이것이 바로 스레드입니다. 각 기능이나 객체를 각 각 다른 스레드로 나누어 줘서 함께 실행되게 되는거죠. 위 코드는 Thread 클래스로 스레드를 생성한 것입니다.

이번엔 ThreadPool로 Thread를 생성해 보도록 하겠습니다.

private void btnAsyncCall2_Click(object sender, EventArgs e)

{

    TheWorker worker = new TheWorker();

    WaitCallback wcb = new WaitCallback(worker.DoWork2);

    ThreadPool.QueueUserWorkItem(wcb);

    Thread.Sleep(1000);

    MessageBox.Show("스레드풀을 이용했습니다.");

}

위의 Thread를 이용한 것 같이 10초 후에 Dowork 메소드가 완료되고 실행된 1초 후에 메시지를 뛰우는 코드입니다. 동작은 위 코드와 똑같기 때문에 작동 설명은 드리지 않겠습니다.
ThreadPool 클래스를 이용할 때는 ThreadPool.QueueUserWorkItem()를 이용하게 됩니다. 두개 이상의 스레드를 생성한다면 저 메소드를 더 생성하면 되겠죠. WaitCallback 는 스레드풀 스레드가 실행될때 호출되는 콜백 함수입니다. QueueUserWorkItem 메소드는 WaitCallback 델리게이트 인자를 요구하지요.

이번에는 Timer 클래스를 이용한 스레드 생성을 보도록 하겠습니다.

private void btnAsyncCall3_Click(object sender, EventArgs e)

{

    int i = 0;

    System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

    timer.Interval = 2000;

    timer.Tick += delegate(object sender2, EventArgs e2)

    {

        if (i < 10)

        {

            Console.WriteLine("작업이 처리되었습니다");

            i++;

        }

        else

        {

            timer.Dispose();

            Console.WriteLine("작업완료");

        }

    };

    timer.Start();

}

위소스를 보면 알 수 있듯이 Timer를 생성해서 2초 마다 발생하게 처리했습니다. 그렇게 10번 처리 되면 timer를 종료하지요. Timer로 스레드르 생성하는 방법은 System.Windows.Forms.Timer 외에도 System.Timers.Timer, System.Threading.Timer가 있습니다.

마지막에 적어놓은 동기화는 멀티프로세싱에서 문제중 하나인 변수 공유에 대한 문제 처리를 말합니다. 이 처리가 제대로 되지 않는다면 공유변수에 이상한 값이 들어가 큰문제가 발생할 수 있겠지요.
이문제 처리를 어떻게 하는지 한번 보시겠습니다.

private void btnSynchronize_Click(object sender, EventArgs e)

{

    TheWorker worker1 = new TheWorker();

    TheWorker worker2 = new TheWorker();

 

    Thread thread1 = new Thread(new ThreadStart(worker1.DoSum));

    Thread thread2 = new Thread(new ThreadStart(worker2.DoSum));

 

    thread1.IsBackground = true;

    thread2.IsBackground = true;

 

    thread1.Start();

    thread2.Start();

}


class TheWorker

{

    private static int result;

    private static AutoResetEvent are = new AutoResetEvent(true);

    public void DoSum()

    {

        are.WaitOne();  //신호상태 대기, 진입후 비신호상태로 변경

        {

            result = 0;

            for (int i = 0; i <= 100; i++)

            {

                result += i;

                if (i % 100 == 0)

                    Thread.Sleep(1000);

            }

            Console.WriteLine("1부터 1000까지의 합 : " + result);

        }

        are.Set(); //신호받음 상태로 변경

    }

}

AutoResetEvent를 이용하여 초기상태를 받아오게 됩니다. 그리고 한 객체가 들어와서 변수를 쓰고 있으면 해당 변수에 접근할 수 없도록 닫아 버리고 해당 변수의 처리가 끝나면 Set() 메소드를 이용하여 처리 완료를 알리고 다른 Thread에서 처리를 할 수 있도록 처리 해주는 겁입니다. 이렇게 함으로써 동기화문제를 처리하는 것이죠.


Thread에 대해서 아주 간단하고 쉬운 예제로 알아보았는데요. 많은 도움이 되셨는지는 모르겠습니다. 내용이 많이 부족해서요. C#을 처음 접하시는 분이나 아직 스레드에 대해서 잘 모르겠다 싶은 분들에게 좀이라도 도움이 될까 싶어서 이렇게 포스팅 해봅니다.


더 좋은 내용의 포스팅을 약속 드립니다 ^^


엔젤루스였습니다~

728x90
반응형

'.NET > C#' 카테고리의 다른 글

try~catch문(예외처리)  (3) 2010.10.14
스레드 정보 알아오기...  (0) 2010.10.12
C# 소수(Prime Number) 찾기  (0) 2010.10.12
C# 프로세스 목록 알아오기  (1) 2010.10.11
Windows Forms의 시작  (2) 2010.01.19
C# Event  (0) 2010.01.18
C# delegate  (0) 2010.01.14
FileAndDirectory  (0) 2010.01.12
C# File Input/Output  (1) 2009.12.24
C# Collection  (0) 2009.12.21