본문 바로가기
c#

[c#] 스레드(Thread)사용법 및 예제

by devjh 2020. 7. 30.
반응형

개요
1. Thread
2. lock
3. BeginInvoke(비동기, 스레드풀)
4. BeginInvoke2(비동기 리턴값)
5. BeginInvoke3(콜백)
6. BeginInvoke4(BeginInvoke 예제)



이번 게시글에서는 스레드에 대해 정리합니다.

들어가기 앞서 프로세스와 스레드가 무엇인지 간단히 알아보고 코드를 보겠습니다.

프로세스란 메모리에 적재된 실행되는 프로그램을 말하며(프로세스는 데스크탑 애플리케이션, 백엔드 애플리케이션 그 자체를 말합니다)

스레드란 프로세스 내부에서 생성되는 실제로 작업을 하는 주체를 말합니다.

하나의 프로세스는 여러개의 스레드를 가질 수 있으며

같은 프로세스안에 있는 스레드들은 코드, 데이터, 힙 영역은 공유하고 스택은 각각 따로 할당된다는 특징이 있습니다.(힙 영역의 데이터를 공유시 주의)


예제는 스레드를 생성하고 실행하는데 촛점을 두고있지만, 스레드는 new Thread키워드로만 생성되는건 아닙니다.

 

c#으로 데스크탑 애플리케이션을 만들면 스레드가 화면을 보여주기 위해 계속 일하게 되고, 버튼에 대한 요청은 스레드가 진행하며 api서버를만들면 http request 하나하나는 스레드가 실행시켜 줍니다.

 

1. 스레드사용하기

namespace ThreadExample
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            Thread myThread = new Thread(Func); 
            myThread.Start(); 
            for (int i = 0; i < 3; i++) 
            { 
                Console.WriteLine(i+1);
                Thread.Sleep(100); 
            } 
            Console.WriteLine("메인쓰레드 종료");
        } 
            
        private static void Func() 
        { 
            for (int i = 0; i < 10; i++) 
            { 
                Console.WriteLine(i + 1); 
                Thread.Sleep(100); 
            } 
        } 
    } 
}

new 키워드를 사용하여 스레드를 생성합니다.(매개변수는 스레드가 실행할 메서드)

Start 키워드를 사용해 스레드를 실행시킵니다.

 

메인스레드가 3까지 센후 종료되어도 스레드가 할당되었기에 서브스레드가 10까지 센 이후에 프로그램이 종료됩니다.

2. 스레드에 매개변수 전달하기

namespace ThreadExample 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            Thread myThread = new Thread(Func);
            myThread.Start(7); 
            for (int i = 0; i < 3; i++) 
            { 
                Console.WriteLine(i+1); 
                Thread.Sleep(100); 
            } 
            Console.WriteLine("메인쓰레드 종료"); 
        } 
        private static void Func(object obj) 
        { 
            int num = (int)obj; 
            for (int i = 0; i < num; i++) 
            { 
                Console.WriteLine(i + 1); 
                Thread.Sleep(100); 
            } 
        } 
    } 
}

매개변수는 위의 방법으로 전달받아 사용합니다.

스레드를 생성할때 넣는것이 아니라 start키워드의 매개변수에 넣어줘야하며
Thread myThread = new Thread(Func(7)) -> X
myThread.Start(7) -> O

스레드가 실행할 메서드에서는 object형으로 받아온 후 형변환하여 사용하여야 합니다.

3. 스레드에 2개이상의 매개변수 전달하기

namespace ThreadExample 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            Thread myThread = new Thread(Func); 
            ThreadParam tp = new ThreadParam(2, 6); 
            myThread.Start(tp); 
            for (int i = 0; i < 3; i++) 
            { 
                Console.WriteLine(i+1); 
                Thread.Sleep(100); 
            } 
            Console.WriteLine("메인쓰레드 종료"); 
        } 
        private static void Func(object obj) 
        { 
            ThreadParam tempParam = obj as ThreadParam; 
            int num = tempParam.param1 + tempParam.param2; 
            for (int i = 0; i < num; i++) 
            { 
                Console.WriteLine(i + 1); 
                Thread.Sleep(100); 
            } 
        } 
    } 
    class ThreadParam 
    { 
        public int param1; 
        public int param2; 
        public ThreadParam(int num1, int num2) 
        { 
            this.param1 = num1; this.param2 = num2; 
        } 
    } 
}


객체를 하나 만들어서 넘겨준후 형변환 후 사용합니다.



4. 백그라운드 스레드

namespace ThreadExample 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            Thread myThread = new Thread(Func);
            myThread.IsBackground = true; 
            myThread.Start(); 
            for (int i = 0; i < 3; i++) 
            { 
                Console.WriteLine(i+1); 
                Thread.Sleep(100); 
            }
            Console.WriteLine("메인쓰레드 종료"); 
        } 
        private static void Func() 
        { 
            for (int i = 0; i < 10; i++) 
            {
                Console.WriteLine(i + 1); 
                Thread.Sleep(100); 
            } 
        }
    }
}

스레드를 백그라운드로 만들면 프로세스가 종료될때

프로세스가 백그라운드스레드를 종료시켜버립니다.(프로세스가 스레드 종료를 관리)

11 22 33 까지 출력되고 메인스레드가 종료되어 프로그램이 종료됩니다.

 

5. 스레드 기다리기1(블락시키기)

namespace ThreadExample 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            Thread myThread = new Thread(Func); 
            myThread.Start(); 
            for (int i = 0; i < 3; i++) 
            { 
                Console.WriteLine(i+1); 
                Thread.Sleep(100); 
            } 
            myThread.Join(); 
            Console.WriteLine("메인 스레드 종료"); 
        } 
        private static void Func() 
        { 
            for (int i = 0; i < 10; i++) 
            { 
                Console.WriteLine(i+1); 
                Thread.Sleep(100); 
            } 
        }
    }
}

서브스레드를 실행시키고 메인스레드를 블락시켜 서브스레드가 끝날때까지 기다리는 방법입니다.

메인스레드는 Join에서 블락되어 서브스레드가 끝날때까지 대기합니다.

 

6. 스레드 기다리기2(블락시키기)

namespace ThreadExample 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.ManualReset); 
            Thread myThread = new Thread(Func); 
            myThread.Start(ewh); 
            Console.WriteLine("서브스레드 시작"); 
            for (int i = 0; i < 3; i++) 
            { 
                Console.WriteLine(i+1); 
                Thread.Sleep(100); 
            } 
            ewh.WaitOne(); 
            Console.WriteLine("메인쓰레드 종료"); 
        }
        private static void Func(object obj) 
        { 
            EventWaitHandle ewh = obj as EventWaitHandle; 
            for (int i = 0; i < 10; i++) 
            { 
                Console.WriteLine(i + 1); 
                Thread.Sleep(100); 
            } 
            ewh.Set();
        }
    }
}


작업스레드를 실행시키고 메인스레드를 블락시키고 스레드가 끝나길 기다리는 방법입니다.

메인스레드는 ewh.WaitOne()에서 블락되어 해당스레드가 끝날때까지 대기합니다.

WaitOne은 스레드쪽보다는 비동기작업의 IAsyncResult 와 관련이 깊습니다만

Thread에서도 사용가능하므로 사용해봤습니다.

추후 IAsyncResult를 다룰때 다시 다루겠습니다.

 

7. 폴링

namespace ThreadExample 
{ 
    class Program 
    { 
        static bool myFlag = false; 
        static void Main(string[] args) 
        { 
            Thread myThread = new Thread(Func); 
            myThread.Start(); 
            for (int i = 0; i < 3; i++) 
            { 
                Console.WriteLine(i+1); 
                Thread.Sleep(100); 
            } 
            myFlag = true; 
            Console.WriteLine("메인스레드 종료"); 
        } 
        private static void Func() 
        { 
            while (true) 
            { 
                if(myFlag) 
                { 
                    Console.WriteLine("폴링 성공"); 
                } 
                Thread.Sleep(100); 
            } 
        }
    } 
}


가장 보편적인 스레드 사용법입니다.

로직은 아래와 같습니다.

1. 서브스레드를 만들어줍니다.

2. 서브스레드에서 돌아갈 메서드를 무한루프화 시키고 해당조건이 만족됐는지 0.1초 간격으로 체크해줍니다.

3. 서브스레드는 flag가 ture가 되면 "폴링 성공"을 출력해줍니다.

4. 메인스레드는 서브스레드를 시작해주고 3까지 출력해준후 flag를 true로 만들어줍니다.

5. 서브스레드는 종료조건이 없으니 메인스레드가 끝나도 프로그램은 종료되지않고 "폴링성공"을 계속 출력합니다.

 

8. 스레드 강제종료

namespace ThreadExample 
{ 
    class Program 
    { 
        static bool myFlag = false; 
        static void Main(string[] args) 
        { 
            Thread myThread = new Thread(Func); 
            myThread.Start(); 
            for (int i = 0; i < 3; i++) 
            { 
                Console.WriteLine(i + 1);
                Thread.Sleep(100); 
            } 
            myThread.Abort(); 
            myFlag = true; 
            Console.WriteLine("메인스레드 종료"); 
        } 
        private static void Func() 
        { 
            while (true) 
            { 
                if (myFlag) 
                { 
                    Console.WriteLine("폴링 성공"); 
                } 
                Thread.Sleep(1000);
            } 
        } 
    } 
}

Abort키워드로 서브스레드를 강제종료 시킬 수 있습니다.

myFlag는 True가 되었지만 이미 폴링스레드가 종료됐으므로 "폴링 성공"은 콘솔에 출력되지 않습니다.

 

반응형

댓글